135 Commits

Author SHA1 Message Date
Mario Vavti
a532bd9cf1 bugfix release 1.14.2 2016-10-18 21:02:10 +02:00
zotlabs
f2d1f1efd6 issue #553 - negative birthday from directory. This may not completely fix it and may take a profile update to register the change; but it's a start; specifically we need to store 0000-00-00 in the xprof table of the directory server if that's what we get over the wire. Birthday fields are string and are not subject to SQL strict_mode on dates. We want these to be the string '0000-00-00' if not set to a valid date. 2016-10-18 20:55:24 +02:00
zotlabs
a7fd4e96f1 issue #549, ACL has 'public' selected even when restrictive ACL is being used. 2016-10-17 23:39:53 -07:00
zotlabs
a9cae7c9bd roll std_version 2016-10-14 14:05:11 -07:00
zotlabs
ebd92d736a permissions issue 2016-10-14 13:20:48 -07:00
zotlabs
96b7bfb32c remove logging 2016-10-14 13:19:42 -07:00
Mario Vavti
ce0f98989c Release version 1.14 2016-10-12 12:23:34 +02:00
Mario Vavti
cf547be1d6 Merge branch '1.14RC' 2016-10-12 12:13:44 +02:00
Mario Vavti
16da1a4e81 update changelog 2016-10-12 11:23:27 +02:00
Mario Vavti
050c0752f9 fix connected time not shown on ajax loaded connections 2016-10-12 10:59:19 +02:00
Mario Vavti
205bc96827 make diaspora w2w appear as a quote to make a little bit more clear what is happening 2016-10-11 10:30:49 +02:00
zotlabs
2bd61aed7a spaghetti 2016-10-11 10:19:32 +02:00
zotlabs
81e704648f api issues 2016-10-10 23:25:01 -07:00
zotlabs
e75b0cb743 Merge branch '1.14RC' of https://github.com/redmatrix/hubzilla into 1.14RC_merge 2016-10-10 22:47:19 -07:00
zotlabs
29617737ca Merge branch 'master' of https://github.com/redmatrix/hubzilla into master_merge 2016-10-10 22:46:43 -07:00
zotlabs
26cc73118a don't expire posts before 2001 2016-10-10 22:46:24 -07:00
zotlabs
47e91e0660 don't expire posts before 2001 2016-10-10 22:45:01 -07:00
Mario Vavti
18ef8ea271 changelog and version 1.14RC1 2016-10-10 20:16:15 +02:00
Mario Vavti
ad26eec9f2 bump micro version 2016-10-10 14:41:06 +02:00
Mario Vavti
3b422406a9 hotfix for readmore.js 2016-10-10 14:38:45 +02:00
Mario Vavti
79a068e92b another fix to readmore.js and update patch file 2016-10-10 14:27:49 +02:00
Mario Vavti
717a532c09 fix readmorejs collapsing on scrolldirection change in mobile browsers 2016-10-10 13:43:29 +02:00
redmatrix
49fd53ee67 try naked embed before submission instead of at render time 2016-10-06 16:00:41 -07:00
redmatrix
1ad4d26f31 Merge branch '1.14RC' of https://github.com/redmatrix/hubzilla into 1.14RC_merge 2016-10-05 17:28:12 -07:00
redmatrix
2a02b6de44 update hook documentation 2016-10-05 17:25:49 -07:00
redmatrix
21a0498a30 new hook bbcode_filter 2016-10-05 17:21:32 -07:00
jeroenpraat
411d7aa6c4 Updating strings for it, nl and es.. 2016-10-05 22:29:57 +02:00
Mario Vavti
619c79df27 bugfixrelease fullcalendar-3.0.1 2016-10-05 20:39:50 +02:00
redmatrix
246b2c0d1b remove leftover rating fragment 2016-10-04 19:47:46 -07:00
Mario Vavti
c089d30915 feature_enabled() only takes two arguments 2016-10-04 21:11:11 +02:00
redmatrix
4b91d4b5c3 wrong resource (attach_change_permissions()) 2016-10-03 17:24:21 -07:00
redmatrix
2aa8979522 Merge branch '1.14RC' of https://github.com/redmatrix/hubzilla into 1.14RC_merge 2016-10-03 16:01:52 -07:00
redmatrix
e93fdefd72 return the email_sent status 2016-10-03 16:01:43 -07:00
Mario Vavti
5dc9de41eb update changelog 2016-10-03 12:04:07 +02:00
Mario Vavti
5cd4e340eb another missing backslash 2016-10-02 10:48:02 +02:00
Mario Vavti
541e40f29c missing backslash 2016-10-02 10:36:56 +02:00
redmatrix
1af56b1025 sync cloud storage permission changes (issue #538 continued) 2016-10-02 10:05:42 +02:00
Mario Vavti
27d5b9cfd0 update changelog 2016-10-02 10:05:23 +02:00
redmatrix
771d87781e roll version 2016-10-01 15:47:03 -07:00
redmatrix
dab3495751 check datatype to be sure 2016-09-28 02:16:05 -07:00
redmatrix
ac6c43b5fb translate null_date on all db fields 2016-09-26 18:59:01 -07:00
redmatrix
56f66ce001 issue #511, postgres schema issues 2016-09-09 21:30:24 -07:00
redmatrix
57033bb599 custom/expert permissions bug 2016-09-06 16:18:06 -07:00
redmatrix
760427fc43 fix aconfig 2016-08-28 16:18:31 -07:00
redmatrix
47bd97b55d doc search broken 2016-08-27 15:31:26 -07:00
redmatrix
de1e39add9 Merge branch 'master' of https://github.com/redmatrix/hubzilla into master_merge 2016-08-27 05:07:23 -07:00
redmatrix
ebd3b965fc vsprintf error on sql query 2016-08-27 05:07:07 -07:00
git-marijus
1d455c21d4 Merge pull request #499 from mjfriaza/master
Add new page 'connedit' to Spanish context help & fixed a string in Spanish translation
2016-08-26 19:58:00 +02:00
Mario Vavti
8f631d0693 contact-block needs a class clear div at the end to not mess with following widgets and whitespace 2016-08-26 19:52:21 +02:00
Manuel Jiménez Friaza
0381102c27 Fixed a string in the Spanish translation 2016-08-25 12:20:53 +02:00
Manuel Jiménez Friaza
e81dccb14e Fixed a string in the Spanish translation 2016-08-25 12:20:22 +02:00
Manuel Jiménez Friaza
5c3e6307b4 Add new page connedit to Spanish context help 2016-08-25 12:14:10 +02:00
redmatrix
8aee932525 version 2016-08-23 00:46:22 -07:00
redmatrix
8b737e9610 Merge branch 'master' into 1.12RC 2016-08-22 23:02:38 -07:00
redmatrix
0b16a5531a Revert "Merge branch '1.12RC'"
This reverts commit b89c869e7c, reversing
changes made to fbb357ac47.
2016-08-22 20:31:31 -07:00
redmatrix
b7fbd0ee50 Revert "missing release file"
This reverts commit 17fa2d8801.
2016-08-22 20:30:39 -07:00
redmatrix
2afdb7854b Revert "more missing files from merge"
This reverts commit 1bd784cf12.
2016-08-22 20:30:17 -07:00
redmatrix
1bd784cf12 more missing files from merge 2016-08-22 20:19:31 -07:00
redmatrix
17fa2d8801 missing release file 2016-08-22 20:17:37 -07:00
redmatrix
b89c869e7c Merge branch '1.12RC' 2016-08-22 20:05:25 -07:00
redmatrix
1a506ad49c Merge branch '1.12RC' of https://github.com/redmatrix/hubzilla into 1.12RC_merge 2016-08-22 17:06:56 -07:00
redmatrix
b0d3c17f19 public forum fallback checking (when custom/expert permissions are applied) was looking at owner rather than observer perms 2016-08-22 17:06:40 -07:00
jeroenpraat
f4507d878d Spanish and Dutch strings 2016-08-22 19:45:46 +02:00
redmatrix
d4ef3c183c Merge branch '1.12RC' of https://github.com/redmatrix/hubzilla into 1.12RC_merge 2016-08-21 16:29:41 -07:00
redmatrix
5c3b06b8a9 issue #496 2016-08-21 16:29:16 -07:00
jeroenpraat
15d9bf4ebe Updating Spanish and Dutch strings 2016-08-17 17:37:21 +02:00
hubzilla
6dd4e9ac60 Merge pull request #493 from phellmes/de20160817
Update DE translation strings
2016-08-17 20:44:05 +10:00
phellmes
2b0c2891e3 Update DE translation strings 2016-08-17 10:39:21 +02:00
redmatrix
fbb357ac47 Use double quotes for strings passed via templates to javascript. Some translated strings have unescaped single quotes. 2016-08-15 17:20:40 -07:00
redmatrix
38de583db0 Use double quotes for strings passed via templates to javascript. Some translated strings have unescaped single quotes. 2016-08-15 17:20:29 -07:00
redmatrix
055ee75302 Merge branch '1.12RC' of https://github.com/redmatrix/hubzilla into 1.12RC_merge 2016-08-15 13:19:34 -07:00
Einer von Vielen
f95011a565 Merge dev with last changes of homeinstall script in master 2016-08-15 13:19:20 -07:00
Mario Vavti
6bb5ea7a81 Merge branch '1.12RC' of https://github.com/redmatrix/hubzilla into 1.12RC 2016-08-15 11:22:20 +02:00
Mario Vavti
efcde8f3dd update bootstrap 3.3.5 > 3.3.7 2016-08-15 11:21:57 +02:00
Mario Vavti
057266653b fix javascript error if not logged in 2016-08-15 11:20:51 +02:00
Mario Vavti
2755c74c29 btn/dropdown rendering issue 2016-08-14 11:31:44 +02:00
hubzilla
5ad5afe63b Merge pull request #481 from anaqreon/issue-475
Fixed bug preventing images in root photo album from loading in embed…
2016-08-14 09:54:12 +10:00
Mario Vavti
2c3843ee4c remove js debugging 2016-08-13 22:28:40 +02:00
Mario Vavti
e5d1dd111e fix #480, enable bbcode autocomplete for photo comments and remove some redundant javascript 2016-08-13 22:20:54 +02:00
Andrew Manning
125713e938 Fix for older photos not being embedded by the embedphotos tool. 2016-08-13 14:38:13 -04:00
Andrew Manning
e128ff4e8f Fixed bug preventing images in root photo album from loading in embedphotos dialog. 2016-08-13 14:20:41 -04:00
redmatrix
2c8a82713e pending flag not being reset when using autoperms from custom role 2016-08-12 18:18:35 -07:00
zottel
7d2a17ea6e fix auto-connect setting 2016-08-12 14:44:48 -07:00
Mario Vavti
91b8c769bd possible quickfix for multi-acl not honoring jotnets 2016-08-12 14:44:47 -07:00
redmatrix
0637a71669 embed filter adjustments 2016-08-11 17:34:05 -07:00
redmatrix
45dc995967 forum detection was off for forums with custom perms 2016-08-10 20:50:32 -07:00
redmatrix
d8240a40b7 update CHANGELOG for 1.12 2016-08-10 17:35:07 -07:00
redmatrix
adf34fb201 update version 2016-08-10 16:54:57 -07:00
redmatrix
51c610de73 issue #474 2016-08-03 21:43:19 -07:00
redmatrix
382ce4cc61 issue #473 - unable to delete privacy groups 2016-08-03 16:11:15 -07:00
redmatrix
8f2106da2b Merge branch 'master' of https://github.com/redmatrix/hubzilla into master_merge 2016-08-02 19:33:09 -07:00
redmatrix
1e988b1fea missing comma in atoken update sql 2016-08-02 19:32:39 -07:00
hubzilla
14ca376902 Merge pull request #472 from einervonvielen/homeinstall-remove-owncloud
Homeinstall remove owncloud
2016-08-01 09:14:24 +10:00
hubzilla
77a9be845d Merge pull request #471 from einervonvielen/homeinstall-fix-letsencrypt-config-path
Homeinstall fix letsencrypt config path
2016-08-01 09:14:08 +10:00
hubzilla
9dd9e27fa8 Merge pull request #470 from einervonvielen/einervonvielen-fix-homeinstall-git-path
homeinstall - fix - path to git clone
2016-08-01 09:13:57 +10:00
Einer von Vielen
b60e36ea7a Changed homeinstall removed installation of owncloud from install
script
2016-07-31 18:53:28 +02:00
Einer von Vielen
fae7993f93 Fixed homeinstall script.
parameter --config has to be used after a change of letsencrpyt.sh
2016-07-31 18:38:45 +02:00
Einer von Vielen
215659a234 Merge remote-tracking branch 'mikemaster/master' 2016-07-31 18:05:16 +02:00
einervonvielen
c0e0379bab Merge pull request #2 from einervonvielen/einervonvielen-fix-homeinstall-git-path
homeinstall - fix - path to git clone
2016-07-31 15:27:45 +02:00
einervonvielen
c761531947 homeinstall - fix - path to git clone 2016-07-31 15:26:02 +02:00
jeroenpraat
287e9c8d68 Updating es-es strings 2016-07-29 14:24:20 +02:00
redmatrix
ae5c10a71c Merge branch '1.10RC' 2016-07-28 19:58:05 -07:00
redmatrix
4d5202353f Merge branch '1.10RC' of https://github.com/redmatrix/hubzilla into 1.10RC_merge 2016-07-28 18:26:32 -07:00
redmatrix
142bcd6806 move fixes to po2php forward to 1.10 2016-07-28 18:26:06 -07:00
hubzilla
7075697f60 Merge pull request #465 from phellmes/de20160728
Update DE translation strings
2016-07-29 09:55:47 +10:00
redmatrix
856133b07d fix it strings after merge stuffup 2016-07-28 16:53:18 -07:00
redmatrix
0b2d809309 github didn't accept the last push. Touching the files to force a git revision 2016-07-28 16:47:08 -07:00
jeroenpraat
ec383aca03 Updated language files for release 2016-07-28 16:37:19 -07:00
redmatrix
bc74425872 Revert "Merge branch '1.10RC' of https://github.com/redmatrix/hubzilla into 1.10RC_merge"
This reverts commit 35f17acb38, reversing
changes made to 58cf5f310d.
2016-07-28 16:02:51 -07:00
redmatrix
35f17acb38 Merge branch '1.10RC' of https://github.com/redmatrix/hubzilla into 1.10RC_merge 2016-07-28 13:15:10 -07:00
redmatrix
58cf5f310d sql typo 2016-07-28 13:14:48 -07:00
redmatrix
2d916b531b Revert "issue #466, sql typo"
This reverts commit 4ce8f965aa.
2016-07-28 13:13:59 -07:00
redmatrix
12952c9821 issue #466, sql typo 2016-07-28 13:11:01 -07:00
jeroenpraat
47e1c4e059 Updated language files for release 2016-07-28 21:33:02 +02:00
jeroenpraat
47119ddc7d Merge branch '1.10RC' of https://github.com/redmatrix/hubzilla into 1.10RC 2016-07-28 21:07:46 +02:00
phellmes
18f0961caa Update DE translation strings 2016-07-28 14:09:29 +02:00
Mario Vavti
54ecf0f45f URLUtil path has changed since sabredav 1.8 - fixes renaming issue in dav clients 2016-07-26 09:05:38 +02:00
redmatrix
24d28bc23a issue #460 2016-07-25 13:29:06 -07:00
redmatrix
79eeeaee95 fix birthday addtocal 2016-07-21 21:36:10 -07:00
redmatrix
dca8a05026 Merge branch '1.10RC' of https://github.com/redmatrix/hubzilla into 1.10RC_merge 2016-07-21 20:06:08 -07:00
redmatrix
5cfc286972 zat URL auth updated to match changes to the atoken_login interface 2016-07-21 20:06:02 -07:00
hubzilla
2d4b75428a Merge pull request #452 from anaqreon/patch-1
Fixed whitescreen when creating folders via cloud files web interface
2016-07-17 19:40:26 +10:00
Andrew Manning
db2b6f1268 Fixed whitescreen when creating folders via cloud files web interface
I'm getting a "not found" error when I try to create folders in the cloud files web interface. I click the "Create" button beside the "Upload" button, type a bland name for a new folder, and I get a white screen. Including the attach.php file fixes the bug.

Version 1.9.1+99354ac
2016-07-16 21:23:29 -04:00
Habeas Codice
983ccef87c NOT NULL constraint without default and INSERT does not provide one 2016-07-11 10:55:42 -07:00
Mario Vavti
ad83825d4f missing backslash leads to wsod on refresh permissions 2016-06-29 22:50:40 +02:00
Habeas Codice
45f5ac560d typos 2016-06-27 06:28:08 -07:00
redmatrix
6154fc7686 Merge branch 'master' of https://github.com/redmatrix/hubzilla into master_merge 2016-06-25 04:11:03 -07:00
redmatrix
1924459abd media (e.g. video) files weren't being detected correctly in oembed, causing the stream to try and load large videos (and failing) 2016-06-25 04:10:48 -07:00
hubzilla
7470fc7f24 Merge pull request #437 from solstag/help
Improvements to help pages
2016-06-25 20:24:28 +10:00
Alexandre Hannud Abdo
0e041a3b64 Improvements to help pages 2016-06-25 00:18:24 +00:00
redmatrix
1577efa25e fix pdledit list mode 2016-06-24 02:17:25 -07:00
redmatrix
f7d2c99a3a move revision up 2016-06-22 20:18:29 -07:00
redmatrix
32408ed6a3 missing class selector when "use photo as profile photo" 2016-06-22 20:18:08 -07:00
redmatrix
76f07a7f97 Merge branch 'master' of https://github.com/redmatrix/hubzilla into master_merge 2016-06-20 13:42:51 -07:00
redmatrix
4d219df04e fix siteinfo plugin list 2016-06-20 13:42:39 -07:00
7333 changed files with 749422 additions and 869815 deletions

25
.gitignore vendored
View File

@@ -14,21 +14,19 @@
*.rej
# OSX .DS_Store files
.DS_Store
# version scripts (repo master only)
# version scripts (repo master only)
.version*
Thumbs.db
## Ignore site specific files and folders
## Ignore RedMatrix site specific files and folders
.htconfig.php
favicon.*
addon/
widget/
custom/
/store/
# site apps
apps/
!doc/context/*/apps
# default startpage
home.html
# page header plugin
@@ -46,8 +44,7 @@ doc/html/
.zotshrc
# external repositories for themes/addons
extend/
# files generated by phpunit
tests/results/
## exclude IDE files
# config files and folders from Eclipse
@@ -63,15 +60,11 @@ nbproject/
.idea/
## composer
# locally installed composer binary
composer.phar
# allow composer.lock, as it is required to have a common state
!composer.lock
# vendor/ is managed by composer, no need to include in our repository
# requires new deployment and needs discussion first
# composer files (at the moment composer is not officially supported and only used to add SabreDAV, we should add these)
composer.*
# When we include composer we should exclude vendor/
#vendor/
# Exclude at least some vendor test files, examples, etc. so far
vendor/**/tests/
vendor/**/Test/
# Exclude at least some vendor test files, examples, etc.
vendor/sabre/*/tests/
vendor/sabre/*/examples/

View File

@@ -2,19 +2,10 @@
Run hubzilla-setup.sh for an unattended installation of hubzilla.
The script is known to work without adjustments with
The script is known to work with Debian 8.3 stable (Jessie)
+ Hardware
- Mini-PC with Debian-9.2-amd64, or
- Rapberry 3 with Raspbian, Debian-9.3
+ DynDNS
- selfHOST.de
- freedns.afraid.org
## Disclaimers
- This script does work with Debian 9 only.
- This script has to be used on a fresh debian install only (it does not take account for a possibly already installed and configured webserver or sql implementation).
+ Home-PC (Debian-8.3.0-amd64)
+ DigitalOcean droplet (Debian 8.3 x64 / 512 MB Memory / 20 GB Disk / NYC3)
# Step-by-Step Overwiew
@@ -23,47 +14,30 @@ The script is known to work without adjustments with
Hardware
+ Internet connection and router at home
+ Mini-pc connected to your router (a Raspberry 3 will do for very small Hubs)
+ Mini-pc connected to your router
+ USB drive for backups
Software
+ Fresh installation of Debian 9 (Stretch)
+ Router with open ports 80 and 443 for your Hub
+ Fresh installation of Debian on your mini-pc
+ Router with open ports 80 and 443 for your Debian
## The basic steps (quick overview)
+ Register your own domain (for example at selfHOST) or a free subdomain (for example at freeDNS)
+ Log on to your fresh Debian
+ Log on to your new debian (server)
- apt-get install git
- mkdir -p /var/www
- cd /var/www
- git clone https://github.com/redmatrix/hubzilla.git html
- cd html/.homeinstall
- cp hubzilla-config.txt.template hubzilla-config.txt
- nano hubzilla-config.txt
- Read the comments carefully
- Enter your values: db pass, domain, values for dyn DNS
- Make sure your external drive (for backups) is mounted
- cp .homeinstall/hubzilla-config.txt.template .homeinstall/hubzilla-config.txt
- nano .homeinstall/hubzilla-config.txt
- Enter your values there: db pass, domain, values for dyn DNS
- hubzilla-setup.sh as root
- ... wait, wait, wait until the script is finised
- reboot
+ Open your domain with a browser and step throught the initial configuration of hubzilla.
## Troubleshooting
If the check of the mail address fails when you try to register the very first user in the browser. Do...
cd /var/www/html
util/config system.do_not_check_dns 1
## Optional - Set path to imagemagick
In Admin settings of hubzilla or via terminal
cd /var/www/html
util/config system.imagick_convert_path /usr/bin/convert
# Step-by-Step in Detail
## Preparations Hardware
@@ -72,44 +46,23 @@ In Admin settings of hubzilla or via terminal
### Recommended: USB Drive for Backups
The installation will create a daily backup written to an external drive.
The installation will create a daily backup.
The USB drive must be compatible with the filesystems
If the backup process does not find an external device than the backup goes to
the internal disk.
- ext4 (if you do not want to encrypt the USB)
- LUKS + ext4 (if you want to encrypt the USB)
The backup includes
- Hubzilla DB
- Hubzilla installation /var/www/html
- Certificates for letsencrypt
The USB drive must be compatible with an encrpyted filesystem LUKS + ext4.
## Preparations Software
### Install Debian Linux on the Mini-PC
Download the stable Debian at https://www.debian.org/
(Debian 8 is no longer supported.)
Download the stable Debian at https://www.debian.org/
Create bootable USB drive with Debian on it.You could use
Create bootable USB drive with Debian on it. You could use the programm
unetbootin, https://en.wikipedia.org/wiki/UNetbootin
- unetbootin, https://en.wikipedia.org/wiki/UNetbootin
- or simply the linux command "dd"
Example for command dd...
su -
dd if=2017-11-29-raspbian-stretch.img of=/dev/mmcblk0
Do not forget to unmount the SD card before and check if unmounted like in this example...
su -
umount /dev/mmcblk0*
df -h
Switch off your mini pc, plug in your USB drive and start the mini pc from the
Switch of your mini pc, plug in your USB drive and start the mini pc from the
stick. Install Debian. Follow the instructions of the installation.
### Configure your Router
@@ -126,20 +79,28 @@ You can use subdomains as well
my.cooldomain.org
There are two ways to get a domain...
There are two way to get a domain
### Method 1: Buy a Domain
- buy a domain (recommended) or
- register a free subdomain
...for example buy at selfHOST.de
### Method 1: Get yourself an own Domain (recommended)
The cost are around 10,- € once and 1,50 € per month (2017).
...for example at selfHOST.de
### Method 2: Register a free subdomain
### Method 2 Register a (free) Subdomain
...for example register at freedns.afraid.org
Register a free subdomain for example at
Follow the instructions in .homeinstall/hubzilla-config.txt.
- freeDNS
- selfHOST
WATCH THIS: A free subdomain is not the prefered way to get a domain name. Why?
Let's encrpyt issues a limited number of certificates each
day. Possibly other users of this domain will try to issue a certificate
at the same day as you do. So make sure you choose a domain with as less subdomains as
possible.
## Install Hubzilla on your Debian
@@ -164,7 +125,7 @@ Make the directory for apache and change diretory to it
Clone hubzilla from git ("git pull" will update it later)
git clone https://framagit.org/hubzilla/core html
git clone https://github.com/redmatrix/hubzilla html
Change to the install script
@@ -174,12 +135,10 @@ Copy the template file
cp hubzilla-config.txt.template hubzilla-config.txt
Modify the file "hubzilla-config.txt". Read the instructions there carefully and enter your values.
Change the file "hubzilla-config.txt". Read the instructions there and enter your values.
nano hubzilla-config.txt
Make sure your external drive (for backups) is plugged in and can be mounted as configured in "hubzilla-config.txt". Otherwise the daily backups will not work.
Run the script
./hubzilla-setup.sh
@@ -187,7 +146,7 @@ Run the script
Wait... The script should not finish with an error message.
In a webbrowser open your domain.
Expected: A test page of hubzilla is shown. All checks there should be
Expected: A test page of hubzilla is shown. All checks there shoulg be
successfull. Go on...
Expected: A page for the Hubzilla server configuration shows up.
@@ -203,43 +162,3 @@ Leave db type "MySQL" untouched.
Follow the instructions in the next pages.
Recommended: Set path to imagemagick
- in admin settings of hubzilla or
- via terminal
util/config system.imagick_convert_path /usr/bin/convert
After the daily script was executed at 05:30 (am)
- look at /var/www/html/hubzilla-daily.log
- check your backup on the external drive
- optionally view the daily log under yourdomain.org/admin/logs/
- set the logfile to var/www/html/hubzilla-daily.log
## Note for the Rasperry
The script was tested with an Raspberry 3 under Raspian (Debian 9.3, 2017-11-29-raspbian-stretch.img).
It is recommended to deinstall these programms to avoid endless updates. Use...
sudo apt-get purge wolfram-engine sonic-pi
sudo apt-get autoremove
It is recommended to run the Raspi without graphical frontend (X-Server). Use...
sudo raspi-config
to boot the Rapsi to the client console.
DO NOT FORGET TO CHANGE THE DEFAULT PASSWORD FOR USER PI!
On a Raspian Stretch (Debian 9) the validation of the mail address fails for the very first user.
This used to happen on some *bsd distros but there was some work to fix that a year ago (2017).
So if your system isn't registered in DNS or DNS isn't active do
cd /var/www/html
util/config system.do_not_check_dns 1

View File

@@ -70,17 +70,15 @@ selfhost_pass=
# freedns_key=U1Z6aGt2R0NzMFNPNWRjbWxxZGpsd093OjE1Mzg5NDE5
#
#
freedns_key=
#freedns_key=
###############################################
### OPTIONAL - Backup to external device ######
#
# The script can use an external device for the daily backup.
# The file system of the device (USB stick for example) must be compatible with
#
# - encrypted LUKS + ext4, or
# - ext4
# The file system of the device (USB stick for example) must be compatible
# with encrypted LUKS + ext4
#
# You should test to mount the device befor you run the script
# (hubzilla-setup.sh).
@@ -115,21 +113,27 @@ freedns_key=
# lsof /media/hubzilla_backup
#
# If you leave the following parameters
#
# - "backup_device_name" and
# - "backup_device_pass"
#
# empty the script will create daily backups on the internal disk (which could
# save you as well).
#
# Example: backup_device_name=/dev/sdc1
#
# Leave "backup_device_pass=" empty if the external device is not encrypted.
#
backup_device_name=
backup_device_pass=
###############################################
### OPTIONAL - Owncloud - deprecated ##########
#
# To install owncloud: owncloud=y
# Leave empty if you don't want to install owncloud
#
#owncloud=
###############################################
### OPTIONAL - do not mess with things below ##
# (...if you are not certain)
@@ -156,3 +160,18 @@ mysqlpass=$db_pass
# Example: phpmyadminpass="aber hallo has blanks in it"
phpmyadminpass=$db_pass
# TODO Prepare hubzilla for programmers
# - install eclipse and plugins
# - install xdebug to debug the php with eclipse
# - weaken permissions on /var/www/html
# - manual steps after this script
# * in eclipse: install plugins for php git hub
# * in eclipse: configure firefox (chrome,...) as browser to run with the php debuger
# * in eclipse: switch php debugger from zend to xdebug
# * in eclipse: add local hubzilla github repository
#
# Which user will use eclipse?
# Leave this empty if you do not want to prepare hubzilla for debugging
#
#developer_name=

View File

@@ -114,11 +114,7 @@ function check_sanity {
fi
if [ ! -f /etc/debian_version ]
then
die "Debian is supported only"
fi
if ! grep -q 'Linux 9' /etc/issue
then
die "Linux 9 (stretch) is supported only"x
die "Ubuntu is not supported"
fi
}
@@ -136,17 +132,17 @@ function check_config {
# backup is important and should be checked
if [ -n "$backup_device_name" ]
then
if [ ! -d "$backup_mount_point" ]
then
mkdir "$backup_mount_point"
fi
device_mounted=0
device_mounted=0
if fdisk -l | grep -i "$backup_device_name.*linux"
then
print_info "ok - filesystem of external device is linux"
if [ -n "$backup_device_pass" ]
then
echo "$backup_device_pass" | cryptsetup luksOpen $backup_device_name cryptobackup
if [ ! -d /media/hubzilla_backup ]
then
mkdir /media/hubzilla_backup
fi
if mount /dev/mapper/cryptobackup /media/hubzilla_backup
then
device_mounted=1
@@ -246,11 +242,6 @@ function install_apache {
nocheck_install "apache2 apache2-utils"
}
function install_imagemagick {
print_info "installing imagemagick..."
nocheck_install "imagemagick"
}
function install_curl {
print_info "installing curl..."
nocheck_install "curl"
@@ -262,11 +253,11 @@ function install_sendmail {
}
function install_php {
# openssl and mbstring are included in libapache2-mod-php
# openssl and mbstring are included in libapache2-mod-php5
# to_to: php5-suhosin
print_info "installing php..."
nocheck_install "libapache2-mod-php php php-pear php-curl php-mcrypt php-gd"
sed -i "s/^upload_max_filesize =.*/upload_max_filesize = 100M/g" /etc/php/7.0/apache2/php.ini
sed -i "s/^post_max_size =.*/post_max_size = 100M/g" /etc/php/7.0/apache2/php.ini
nocheck_install "libapache2-mod-php5 php5 php-pear php5-xcache php5-curl php5-mcrypt php5-gd"
php5enmod mcrypt
}
function install_mysql {
@@ -286,17 +277,18 @@ function install_mysql {
# want to be prompted for it then this can be arranged by preseeding the
# DebConf database with the required information.
#
# echo mysql-server mysql-server/root_password password xyzzy | debconf-set-selections
# echo mysql-server mysql-server/root_password_again password xyzzy | debconf-set-selections
# echo mysql-server-5.5 mysql-server/root_password password xyzzy | debconf-set-selections
# echo mysql-server-5.5 mysql-server/root_password_again password xyzzy | debconf-set-selections
#
print_info "installing mysql..."
if [ -z "$mysqlpass" ]
then
die "mysqlpass not set in $configfile"
fi
echo mysql-server mysql-server/root_password password $mysqlpass | debconf-set-selections
echo mysql-server mysql-server/root_password_again password $mysqlpass | debconf-set-selections
nocheck_install "php-mysql mysql-server mysql-client"
echo mysql-server-5.5 mysql-server/root_password password $mysqlpass | debconf-set-selections
echo mysql-server-5.5 mysql-server/root_password_again password $mysqlpass | debconf-set-selections
nocheck_install "php5-mysql mysql-server mysql-client"
php5enmod mcrypt
}
function install_phpmyadmin {
@@ -335,7 +327,6 @@ function install_phpmyadmin {
echo "Include /etc/phpmyadmin/apache.conf" >> /etc/apache2/apache2.conf
fi
service apache2 restart
/etc/init.d/mysql start
}
function create_hubzilla_db {
@@ -464,6 +455,11 @@ function configure_cron_selfhost {
fi
}
function install_git {
print_info "installing git..."
nocheck_install "git"
}
function install_letsencrypt {
print_info "installing let's encrypt ..."
# check if user gave domain
@@ -495,7 +491,7 @@ END
print_info "letsenrypt exists already (nothing downloaded > no certificate created and registered)"
return 0
fi
git clone https://github.com/lukas2511/dehydrated $le_dir
git clone https://github.com/lukas2511/letsencrypt.sh $le_dir
cd $le_dir
# create config file for letsencrypt.sh
echo "WELLKNOWN=$le_dir" > $le_dir/config.sh
@@ -515,11 +511,9 @@ END
then
die "Failed to load $url_http"
fi
# accept terms of service of letsencrypt
./dehydrated --register --accept-terms
# run script dehydrated
# run letsencrypt.sh
#
./dehydrated --cron --config $le_dir/config.sh
./letsencrypt.sh --cron --config $le_dir/config.sh
}
function configure_apache_for_https {
@@ -570,13 +564,17 @@ function check_https {
}
function install_hubzilla {
print_info "installing hubzilla addons..."
cd /var/www/html/
util/add_addon_repo https://framagit.org/hubzilla/addons.git hzaddons
print_info "installing hubzilla..."
# rm -R /var/www/html/ # for "stand alone" usage
cd /var/www/
# git clone https://github.com/redmatrix/hubzilla html # for "stand alone" usage
cd html/
git clone https://github.com/redmatrix/hubzilla-addons addon
mkdir -p "store/[data]/smarty3"
chmod -R 777 store
touch .htconfig.php
chmod ou+w .htconfig.php
install_hubzilla_plugins
cd /var/www/
chown -R www-data:www-data html
chown root:www-data /var/www/html/
@@ -591,6 +589,73 @@ function install_hubzilla {
print_info "installed hubzilla"
}
function install_hubzilla_plugins {
print_info "installing hubzilla plugins..."
cd /var/www/html
plugin_install=.homeinstall/plugin_install.txt
theme_install=.homeinstall/theme_install.txt
# overwrite script to update the plugin and themes
rm -f $plugins_update
echo "cd /var/www/html" >> $plugins_update
###################
# write plugin file
if [ ! -f "$plugin_install" ]
then
echo "# To install a plugin" >> $plugin_install
echo "# 1. add the plugin in a new line and run" >> $plugin_install
echo "# 2. run" >> $plugin_install
echo "# cd /var/www/html/.homeinstall" >> $plugin_install
echo "# ./hubzilla-setup.sh" >> $plugin_install
echo "https://gitlab.com/zot/ownmapp.git ownMapp" >> $plugin_install
echo "https://gitlab.com/zot/hubzilla-chess.git chess" >> $plugin_install
fi
# install plugins
while read -r line; do
[[ "$line" =~ ^#.*$ ]] && continue
p_url=$(echo $line | awk -F' ' '{print $1}')
p_name=$(echo $line | awk -F' ' '{print $2}')
# basic check of format
if [ ${#p_url} -ge 1 ] && [ ${#p_name} -ge 1 ]
then
# install addon
util/add_addon_repo $line
util/update_addon_repo $p_name # not sure if this line is neccessary
echo "util/update_addon_repo $p_name" >> $plugins_update
else
print_info "skipping installation of a plugin from file $plugin_install - something wrong with format in line: $line"
fi
done < "$plugin_install"
###################
# write theme file
if [ ! -f "$theme_install" ]
then
echo "# To install a theme" >> $theme_install
echo "# 1. add the theme in a new line and run" >> $theme_install
echo "# 2. run" >> $theme_install
echo "# cd /var/www/html/.homeinstall" >> $theme_install
echo "# ./hubzilla-setup.sh" >> $theme_install
echo "https://github.com/DeadSuperHero/hubzilla-themes.git DeadSuperHeroThemes" >> $theme_install
fi
# install plugins
while read -r line; do
[[ "$line" =~ ^#.*$ ]] && continue
p_url=$(echo $line | awk -F' ' '{print $1}')
p_name=$(echo $line | awk -F' ' '{print $2}')
# basic check of format
if [ ${#p_url} -ge 1 ] && [ ${#p_name} -ge 1 ]
then
# install addon
util/add_theme_repo $line
util/update_theme_repo $p_name # not sure if this line is neccessary
echo "util/update_theme_repo $p_name" >> $plugins_update
else
print_info "skipping installation of a theme from file $theme_install - something wrong with format in line: $line"
fi
done < "$theme_install"
print_info "installed hubzilla plugins and themes"
}
function rewrite_to_https {
print_info "configuring apache to redirect http to httpS ..."
htaccessfile=/var/www/html/.htaccess
@@ -610,19 +675,25 @@ function rewrite_to_https {
function install_rsnapshot {
print_info "installing rsnapshot..."
nocheck_install "rsnapshot"
# internal disk
cp -f /etc/rsnapshot.conf $snapshotconfig
# internal disk
cp -f /etc/rsnapshot.conf $snapshotconfig
sed -i "/hourly/s/retain/#retain/" $snapshotconfig
sed -i "/monthly/s/#retain/retain/" $snapshotconfig
sed -i "s/^cmd_cp/#cmd_cp/" $snapshotconfig
sed -i "s/^backup/#backup/" $snapshotconfig
echo "backup /var/lib/mysql/ localhost/" >> $snapshotconfig
echo "backup /var/www/html/ localhost/" >> $snapshotconfig
echo "backup /var/www/letsencrypt/ localhost/" >> $snapshotconfig
if [ -z "`grep 'letsencrypt' $snapshotconfig`" ]
then
echo "backup /var/lib/mysql/ localhost/" >> $snapshotconfig
echo "backup /var/www/html/ localhost/" >> $snapshotconfig
echo "backup /var/www/letsencrypt/ localhost/" >> $snapshotconfig
fi
# external disk
if [ -n "$backup_device_name" ]
if [ -n "$backup_device_name" ] && [ -n "$backup_device_pass" ]
then
cp -f /etc/rsnapshot.conf $snapshotconfig_external_device
sed -i "s#snapshot_root.*#snapshot_root $backup_mount_point#" $snapshotconfig_external_device
sed -i "/alpha/s/6/30/" $snapshotconfig_external_device
sed -i "/hourly/s/retain/#retain/" $snapshotconfig_external_device
sed -i "/monthly/s/#retain/retain/" $snapshotconfig_external_device
sed -i "s/^cmd_cp/#cmd_cp/" $snapshotconfig_external_device
sed -i "s/^backup/#backup/" $snapshotconfig_external_device
if [ -z "`grep 'letsencrypt' $snapshotconfig_external_device`" ]
@@ -646,7 +717,7 @@ function configure_cron_daily {
# every 10 min for poller.php
if [ -z "`grep 'poller.php' /etc/crontab`" ]
then
echo "*/10 * * * * www-data cd /var/www/html; php Zotlabs/Daemon/Master.php Cron >> /dev/null 2>&1" >> /etc/crontab
echo "*/10 * * * * www-data cd /var/www/html; php include/poller.php >> /dev/null 2>&1" >> /etc/crontab
fi
# Run external script daily at 05:30
# - stop apache and mysql-server
@@ -659,8 +730,8 @@ echo "#" >> /var/www/$hubzilladaily
echo "echo \" \"" >> /var/www/$hubzilladaily
echo "echo \"+++ \$(date) +++\"" >> /var/www/$hubzilladaily
echo "echo \" \"" >> /var/www/$hubzilladaily
echo "echo \"\$(date) - renew certificate...\"" >> /var/www/$hubzilladaily
echo "bash $le_dir/dehydrated --cron --config $le_dir/config.sh" >> /var/www/$hubzilladaily
echo "echo \"\$(date) - renew certificat...\"" >> /var/www/$hubzilladaily
echo "bash $le_dir/letsencrypt.sh --cron --config $le_dir/config.sh" >> /var/www/$hubzilladaily
echo "#" >> /var/www/$hubzilladaily
echo "# stop hubzilla" >> /var/www/$hubzilladaily
echo "echo \"\$(date) - stoping apache and mysql...\"" >> /var/www/$hubzilladaily
@@ -696,7 +767,9 @@ echo " if mount $backup_device_name $backup_mount_point" >> /var/www/$hub
echo " then" >> /var/www/$hubzilladaily
echo " device_mounted=1" >> /var/www/$hubzilladaily
echo " echo \"device $backup_device_name is now mounted. Starting backup...\"" >> /var/www/$hubzilladaily
echo " rsnapshot -c $snapshotconfig_external_device alpha" >> /var/www/$hubzilladaily
echo " rsnapshot -c $snapshotconfig_external_device daily" >> /var/www/$hubzilladaily
echo " rsnapshot -c $snapshotconfig_external_device weekly" >> /var/www/$hubzilladaily
echo " rsnapshot -c $snapshotconfig_external_device monthly" >> /var/www/$hubzilladaily
echo " echo \"\$(date) - disk sizes...\"" >> /var/www/$hubzilladaily
echo " df -h" >> /var/www/$hubzilladaily
echo " echo \"\$(date) - db size...\"" >> /var/www/$hubzilladaily
@@ -715,22 +788,28 @@ echo " fi" >> /var/www/$hubzilladaily
echo "fi" >> /var/www/$hubzilladaily
echo "if [ \$device_mounted == 0 ]" >> /var/www/$hubzilladaily
echo "then" >> /var/www/$hubzilladaily
echo " echo \"device could not be mounted $backup_device_name. No backup written.\"" >> /var/www/$hubzilladaily
echo " echo \"device could not be mounted $backup_device_name. Using internal disk for backup...\"" >> /var/www/$hubzilladaily
echo " rsnapshot -c $snapshotconfig daily" >> /var/www/$hubzilladaily
echo " rsnapshot -c $snapshotconfig weekly" >> /var/www/$hubzilladaily
echo " rsnapshot -c $snapshotconfig monthly" >> /var/www/$hubzilladaily
echo "fi" >> /var/www/$hubzilladaily
echo "#" >> /var/www/$hubzilladaily
echo "echo \"\$(date) - db size...\"" >> /var/www/$hubzilladaily
echo "du -h /var/lib/mysql/ | grep mysql/hubzilla" >> /var/www/$hubzilladaily
echo "du -h /var/cache/rsnapshot/ | grep mysql/hubzilla" >> /var/www/$hubzilladaily
echo "#" >> /var/www/$hubzilladaily
echo "# update" >> /var/www/$hubzilladaily
echo "echo \"\$(date) - updating dehydrated...\"" >> /var/www/$hubzilladaily
echo "echo \"\$(date) - updating letsencrypt.sh...\"" >> /var/www/$hubzilladaily
echo "git -C /var/www/letsencrypt/ pull" >> /var/www/$hubzilladaily
echo "echo \"\$(date) - updating hubhilla core...\"" >> /var/www/$hubzilladaily
echo "(cd /var/www/html/ ; util/udall)" >> /var/www/$hubzilladaily
echo "git -C /var/www/html/ pull" >> /var/www/$hubzilladaily
echo "echo \"\$(date) - updating hubhilla addons...\"" >> /var/www/$hubzilladaily
echo "git -C /var/www/html/addon/ pull" >> /var/www/$hubzilladaily
echo "bash /var/www/html/$plugins_update" >> /var/www/$hubzilladaily
echo "chown -R www-data:www-data /var/www/html/ # make all accessable for the webserver" >> /var/www/$hubzilladaily
echo "chown root:www-data /var/www/html/.htaccess" >> /var/www/$hubzilladaily
echo "chmod 0644 /var/www/html/.htaccess # www-data can read but not write it" >> /var/www/$hubzilladaily
echo "echo \"\$(date) - updating linux...\"" >> /var/www/$hubzilladaily
echo "apt-get -q -y update && apt-get -q -y dist-upgrade && apt-get -q -y autoremove # update linux and upgrade" >> /var/www/$hubzilladaily
echo "apt-get -q -y update && apt-get -q -y dist-upgrade # update linux and upgrade" >> /var/www/$hubzilladaily
echo "echo \"\$(date) - Backup hubzilla and update linux finished. Rebooting...\"" >> /var/www/$hubzilladaily
echo "#" >> /var/www/$hubzilladaily
echo "reboot" >> /var/www/$hubzilladaily
@@ -806,7 +885,6 @@ update_upgrade
install_curl
install_sendmail
install_apache
install_imagemagick
install_php
install_mysql
install_phpmyadmin
@@ -816,6 +894,7 @@ install_run_selfhost
ping_domain
configure_cron_freedns
configure_cron_selfhost
install_git
install_letsencrypt
configure_apache_for_https
check_https

View File

@@ -1,186 +1,46 @@
#
# Travis-CI configuration file for Hubzilla
#
## configure things
#
# see http://about.travis-ci.org/docs/user/languages/php/ for more hints
language: php
# use newer 'trusty' based distro, old one is 'precise'
dist: trusty
# use docker based containers
sudo: false
# Git branches whitelist to build on Travis CI
branches:
only:
- master
- dev
# whitelist our tags for release deployments e.g. 2.2
- /^\d+\.\d+(\.\d+)?(-\S*)?$/
# Install additional software
addons:
# Install dependencies for generating API documentation with doxygen
apt:
packages:
- doxygen
- doxygen-latex
- graphviz
- ttf-liberation
# enable and start databases on a per job basis
#services:
# - mariadb
# - postgresql
# any PHP version we want to test against, current stable phpunit requires PHP >= 7.0
# list any PHP version you want to test against
php:
- '7.0'
- '7.1'
- '7.2'
# HHVM does not fulfil PHPUnit platform requirements as being compatible with PHP7 yet
#- 'hhvm'
# using major version aliases
# list of environments to test
env:
global:
# used for doxygen deployment script
- DOXYFILE: $TRAVIS_BUILD_DIR/util/Doxyfile
# Uncomment if a newer/specific version of Doxygen should be used
#- DOXY_VER: 1.8.12
# Code Coverage is slow, no need to have it in every build
- PHPUCOV: "--no-coverage"
# use matrix only for PHP and MySQL, all other combinations added through includes
matrix:
# trusty default MySQL 5.6
- DB=mysql MYSQL_VERSION=5.6
# aliased to a recent 5.4.x version
- 5.4
# aliased to a recent 5.5.x version
- 5.5
# aliased to a recent 5.6.x version
- 5.6
# aliased to a recent 7.x version
- 7.0
# aliased to a recent hhvm version
- hhvm
# Matrix configuration details
# optionally specify a list of environments, for example to test different RDBMS
#env:
# - DB=mysql
# - DB=pgsql
# optionally set up exclutions and allowed failures in the matrix
matrix:
fast_finish: true
# Additional check combinations
include:
# PHP7.2, mariadb 10.2
- php: '7.2'
env: DB=mariadb MARIADB_VERSION=10.2 CODECOV=1
# use mariadb instead of MySQL
addons:
mariadb: '10.2'
# PHP7.2, PostgreSQL 9.6
- php: '7.2'
env: DB=pgsql POSTGRESQL_VERSION=9.6 PHPUNITFILE=phpunit-pgsql.xml
# Use newer postgres than 9.2 default
addons:
postgresql: '9.6'
services:
- postgresql
# PostgreSQL 10 with Docker container
- php: '7.2'
env: DB=pgsql POSTGRESQL_VERSION=10 PHPUNITFILE=phpunit-pgsql.xml
sudo: required
services:
- docker
# PHP7.2, old precise distribution with MySQL 5.5
- php: '7.2'
env: DB=mysql MYSQL_VERSION=5.5
dist: precise
services:
- mysql
# MySQL 5.7 with Docker container
- php: '7.2'
env: DB=mysql MYSQL_VERSION=5.7
sudo: required
services:
- docker
# Excludes from default matrix combinations
# exclude:
# - php: hhvm
# env: DB=pgsql # PDO driver for pgsql is unsupported by HHVM (3rd party install for support)
# cache composer downloads between runs
cache:
directories:
- $HOME/.composer/cache
#- $HOME/doxygen/doxygen-$DOXY_VER/bin
#
## execute things
#
before_install:
- travis_retry composer self-update
# Start MySQL 5.7 Docker container, needs some time to come up
- if [[ "$MYSQL_VERSION" == "5.7" ]]; then sudo service mysql stop; docker run -d -p 3306:3306 -e MYSQL_ALLOW_EMPTY_PASSWORD=yes mysql:5.7 && sleep 25 && docker ps; fi
# Start PostgreSQL 10 Docker container, needs some time to come up
- if [[ "$POSTGRESQL_VERSION" == "10" ]]; then sudo service postgresql stop; docker run -d -p 5432:5432 postgres:10-alpine && sleep 35 && docker ps; fi
# Install composer dev libs
install:
- travis_retry composer install --optimize-autoloader --no-progress
allow_failures:
- php: 7.0
- php: hhvm
# execute any number of scripts before the test run, custom env's are available as variables
before_script:
# Use code coverage config for phpunit
- if [[ ! -z $CODECOV ]]; then export PHPUCOV=""; fi
# Some preparation tasks of environment
- ./tests/travis/prepare.sh
# DB specific prepare scripts
- if [[ "$DB" == "mysql" ]]; then ./tests/travis/prepare_mysql.sh; fi
- if [[ "$DB" == "mariadb" ]]; then ./tests/travis/prepare_mysql.sh; fi
- if [[ "$DB" == "pgsql" ]]; then ./tests/travis/prepare_pgsql.sh; fi
#before_script:
# - if [[ "$DB" == "pgsql" ]]; then psql -c "DROP DATABASE IF EXISTS hello_world_test;" -U postgres; fi
# - if [[ "$DB" == "pgsql" ]]; then psql -c "create database hello_world_test;" -U postgres; fi
# - if [[ "$DB" == "mysql" ]]; then mysql -e "create database IF NOT EXISTS hello_world_test;" -uroot; fi
# omitting "script:" will default to phpunit
script:
- ./vendor/bin/phpunit $PHPUCOV -c tests/$PHPUNITFILE
after_success:
- cat tests/results/testdox.txt
# Generate API documentation and prepare for deployment
- ./tests/travis/gen_apidocs.sh
after_failure:
- cat tests/results/testdox.txt
# Deploying release and API documentation to GitHub
before_deploy:
- if [[ "$CODECOV" == "1" ]]; then zip -9 -r -q tests/hubzilla-testresults.zip tests/results; fi
deploy:
- provider: pages
skip_cleanup: true
local_dir: $TRAVIS_BUILD_DIR/doc/html
github_token: $GH_TOKEN
on:
repo: redmatrix/hubzilla
branch: master
condition: '(-n "$GH_TOKEN") && ("$TRAVIS_JOB_NUMBER" == "${TRAVIS_BUILD_NUMBER}.1")'
# add API documentation to release, could also be used to provide full packages if we want to drop vendor from our repo
- provider: releases
skip_cleanup: true
api_key: $GH_TOKEN
file: 'doc/hubzilla-api-documentation.zip'
on:
repo: redmatrix/hubzilla
tags: true
condition: '(-n "$GH_TOKEN") && ("$TRAVIS_JOB_NUMBER" == "${TRAVIS_BUILD_NUMBER}.1")'
# add code coverage and test results to release
- provider: releases
skip_cleanup: true
api_key: $GH_TOKEN
file: 'tests/hubzilla-testresults.zip'
on:
repo: redmatrix/hubzilla
tags: true
condition: '(-n "$GH_TOKEN") && ("$CODECOV" == "1")'
#after_deploy:
#after_script:
# use the $DB env variable to determine the phpunit.xml to use
script: phpunit tests/*php
# configure notifications (email, IRC, campfire etc)
#notifications:
notifications:
# irc: "irc.freenode.org#yourfavouriteroomfortravis"
# a plugin/script to post to a hubzilla channel would be neat here
# a plugin/script to post to a hubzilla channel would be neat here

990
CHANGELOG
View File

@@ -1,993 +1,3 @@
Hubzilla 3.6 (????-??-??)
- Update jquery.timeago library
- Implement Hookable CSP
- ActivityStreams: accept header changes to support plume
- Streamline inconsistencies in addon naming
- SECURITY: hash the session_id in logs
- Update justified gallery library
- Hide channel in /cloud root if channel is hidden in directory
- Add resend option to channel sources tp discard original author.
- Provide flag to exclude privacy groups for federation plugin use in collect_recipients()
- Upgrading from redmatrix is no longer supported
- Deal with htmlentity encoding during authentication workflow
- Rework mod group
- Make droping posts of removed connections more memory efficient
- Refactor getOutainfo() for DAV storage
- Optionally report total available space when uploading
- SECURITY: provide option to disable the cloud 'root' directory and make the cloud module require a target channel nickname
- Add plink and llink to viewsource
- Add new 'filter by name' feature
- Remove network tabs
- New activity filter widget
- New activity order widget
- Make menus editable by visitors with webpage write permissions
- Move forum notifications to notifications
- Move manage privacy groups to the panel channel menu
- Don't remove items that are starred, filed, or that you replied to when removing a connection
- Don't deliver local items more than once
- Make navbar search use the module search function in /network and /channel
- Paint the locks on private activitypub items red. Their privacy model is "slightly" different from hubzillas
- Improve new channel creation workflow
- Add hook 'get_system_apps'
- Implement reset button for jot
- Adjust accept header to make pleroma happy
- Provide a general purpose GDPR document
- Provide function to fetch photo contents from url
- Make get_default_profile_photo() pluggable
- Refactor tags/mentions
- Refactor autocomplete mechanism
- Display pubsites link in info area if invite only
- Add cancel button to editor
- Implement MessageFilter for pubstream and sourced messages
- Add supported protocols to siteinfo
- Allow pdf embeds
- Allow uninstall of plugins which no longer exists via cmdline tool
- Improve the homeinstall script
- Provide easy access to the autoperms setting for forum and repository channels
- Implement admin delete of files, photos and posts
- Allow a different username to be used when importing a channel
- Provide warnings about profile photo and cover photo permissions
- Set the 'force' flag on attach_mkdir when initiated from a DAV operation
Bugfixes
- Fix double file uploads when dropping files into jot
- Fix jot collapsing when drag and drop to open jot
- Fix wrong album name when moving photos
- Fix wrong timestamp localization before first update in mod mail
- Fix post exiration not propagated to other networks (which support it)
- Fix sys channels visible in dirsearch
- Fix remote_self not working correctly
- Fix photos not syncing properly if destination is a postgres site
- Fix wrong hubloc_url for activitypub hublocs
- Fix z_check_dns() for BSD
- Fix not null violation in oauth1
- Fix DB issues with oauth2 on postgresql
- Fix 'anybody authenticated' not correctly handled in can_comment_on_post()
- Fix postgres issue if register mode is set to yes - with approval
- Fix tag search not finding articles
- Fix issue with mentions when markdown post addon is enabled
- Fix duplicate addressbook entries on repeated channel imports
Addons
- Cart: various display improvements
- Cart: make cart work with postgresql DB backend
- Cart: add new hzservice for service_classes
- Cart: add storewide currency settings
- Cart: provide channel app 'Shop' for cart addon
- Cart: implement order updating
- Cart: use CSP hook for paypals checkout.js
- Cart: provide a cancel mechanism for orders
- Cart: add paypal button
- Gallery: new addon to display photo albums with the photoswipe library
- Ldapauth: optionally auto create channel
- Pubcrawl: new setting to ignore ActivityPub recipients in privacy groups
- Diaspora: fix issue with displaying multiple photos
- Pubcrawl: provide plink
- Pubcrawl: hubloc_url should be baseurl, not actor url
- Pubcrawl: deliver restricted posts from hubzilla as direct messages (there is no other way to address only a subset of followers in mastodon)
- Pubcrawl: address comments to a restricted mastodon post to /followers
Hubzilla 3.4.2 (2018-07-19)
- Compatibility fix for future versions
Hubzilla 3.4.1 (2018-06-08)
- Say bye, bye to GitHub and move sourcecode repositories to #^https://framagit.org/hubzilla
- When removing a connection, don't remove items that are starred, filed or replied to
- Do not show archived forums in forum widget
- Fix potential XSS vulnerabilities
- Translation updates
- Fix postresql issue with oauth2
- Improve abconfig queries
- Fix postgresql issue if register mode was set to yes - with approval
Addons
- Diaspora: fix likes of non-contacts not allowed to like allthough diaspora_public_comments is set
- Pubcrawl: fix wrong hubloc url
- Pubcrawl: fix issues with attachments
- Pubcrawl: fetch required item metadata in asfetch_item()
- Cavatar: use cavatar for all default profile photos if enabled
- Pubcrawl: fix peertube video display
- Pubcrawl: fix incoming activitypub comments not getting propagated downstream
- Statistics: fix .well-known/nodeinfo
- Pubsubhubbub: fix postgresql related issues
- Pubcrawl: send the original LD-signature signed activity when distributing comments downstream if we have it
- Cavatar: improve the image creation process
Hubzilla 3.4 (2018-05-04)
- Provide warnings about profile photo and cover photo permissions
- Don't duplicate addressbook entries on repeated channel imports
- Where possible strip zid parameter from links that get pasted into posts so that they will get a correct zid when rendered
- Rename boxy schema to Focus-Boxy
- Rename BS-Default schema to Focus-Light
- Mark simple_* schemas unmaintained and deprecated - they will be removed in next release if nobody steps up to maintain them.
- Implement trending tags for mod pubstream
- Relax restrictions to the design tools menu to allow those with write_pages permission
- Add alt pager to mod moderate
- Show existing cover photo when changing it
- Update to bootstrap lib to version 4.1
- Provide a higher accuracy method for active channels information
- Provide visible star status for starred posts
- Move the thread author menu to the wall item photo
- Accept system_language through either get or post
- Remove recipient name from stored notifications but keep them in emails
- Fix issue of being forced to log back in after leaving a delegated channel
- Implement last commented expiration setting in mod admin
- Create catcloud widget and provide a type option which can include 'cards' or 'articles'
- Modified notifications widget to add the public stream when the current user is allowed to see it only
- Don't provide a connect button for transient identities
- Merge techlevels and features
- Implement auto-save posts and comments in browser using localStorage
- Display directory server in siteinfo.json
- Bring back the dnt policy document
- Implement OAuth2/OpenIDConnect server
- Add basic structure for additional features documentation
- Community tag refactor
- Obscurify chats
- Provide a way to share wiki pages
- Update folder timestamp on uploaded files
- Code optimisations and de-duplication on updating parent commented timestamp
- Turn newmember widget into a feature
- Make list mode work in cards and articles
- Make alt pager work for articles and cards
- Initial support for alternative sort orders on the cloud pages
- Add Ochannel module for testing OStatus bad behaviour
- Add the social - federation permission role
- Update justified gallery lib from 3.6.3 to 3.6.5
Bugfixes
- Fix regression with forum widget unseen count
- Fix issue with imagemagick exif info
- Aonymous comments in StdLimits shouldn't be allowed
- Fix wiki pages not syncing
- Show "Unseen public activity" channel setting when site only public streams are activated
- Fix channel import failing to provide channel_password value
- Fix permalinks to children of articles and cards
- Fix missing year on profile birthday input
- Fix missing login/out buttons for medium screensize
- Preserve existing categories when updating an app from an embed source
- Fix app sellpage not being stored
- Fix tagadelic being overly protective of permissions
- Fix comments not displayed in single card/article view
- Fix anonymous comments bump thread
- Fix pending registrations visible in admin accounts
Addons
Pubcrawl: fix issues with "private" messages
Pubcrawl: fix issues with postgresql
Fuzzloc: new addon to blur your browser location
Pubcrawl: implement follow by webfinger
Cart: new addon which provides online shop functionalities (experimental)
Pubcrawl: implement two-way summary functionality
Wordpress: upgrade incutio xmlrpc library to use hubzilla curl wrapper
Hzfiles: various fixes
Diaspora: support full_name attribute in profile messages
Frphotos: deprecate plugin (keep it for reference)
Webmention: require html5 parser
GNU-Social: provide alternative xchan_url
Diaspora: fix wrong callback function
Diaspora: fix conversion of forum mentions to markdown by providing a !{forum@host} link syntax
Diaspora: fix item title not transferred
Hubzilla 3.2 (2018-03-09)
- Improve rendering of Readme files in plugin settings
- Add pdl file for mod moderate
- Update redbasic theme screenshot
- Restrict mail messages to max_import_size
- Add pdl file for mod thing
- Add federation property to webfinger
- Provide new member widget which sits beneath the notifications for the first 60 days after an account creation
- Rename Addon/Feature settings to Addon Settings
- Move privacy groups to the newly created Access Control and Permissions tab
- Move oauth_client management and guest access tokens to features rather than auto-enabling at various feature levels
- Change undo_post_tagging() to emit quoted tags rather than using underscore replacement if they contain spaces
- Require directory servers to be using some modern form of encryption
- Change icon set from font-awesome to fork-awesome
- Provide opt-out link and text with notification emails
- Alter image selection widget to accept/submit on choose (github issue #979)
- If hide_in_statistics is set, only include the total channels count and no other statistical info in siteinfo.json
- Mark connections where we do not have post_comments permissions with an no entry sign
- Click your own profile photo to change it if loged in
- Remove street address info from the default basic profile fields
- Handle error logging in on cloud page (post method not implemented)
- Cloud 'view-as-tiles' toggle wasn't available for guests and they are the most likely to prefer that view
- Provide DB compatibility for poll and voting implementations across several platforms
- Remove the unused ZotDriver and ProtoDriver classes
- Move dreport from zot to lib
- Move Zotlabs\Zot\Verify to Zotlabs\Lib\Verify as part of the zot6 re-org
- Add event resource_id to iconfig so Diaspora can search on it without looking inside JSON objects
- Trim non-existent/deprecated plugins from siteinfo plugin list
- Add 'Validate' button to new_channel page
- Do not show summary if it is equal to body
- Update code tag styling so bbcode [code] blocks and wiki markdown inline code render nicely
- Crypto improvements (use pkcs1_oaep_padding instead of the older pkcs1_padding)
- Refactor OAuth2Server a bit
- Refactor of the DB update system
- Extend the oauth2 storage driver so that we can use our own channel table
- Provide option to block the public stream unless authenticated
- Refactor shares and urn shares into activities
- Show likes and dislikes in notices if always_show_in_notices is set
- Add hidden config to disallow anonymous comments (github issue #972)
- Add flexibility to prefix/suffix string translations for jquery.timeago
- Make post titles searchable (github issue #975)
- Implement zot6 delivery
- Remove mobile_detect library
- Separate the parsing of author information from the parsing of item/activity information in feedutils
- Provide summaries in feeds under very limited cases
- Redirect to the email_validation page if login was attempted after account creation but prior to successful verification
- Iprove workflow for form based email validation when auto_channel_create is in effect
- Provide a default video image if nothing else is available
- Surface the ability to change the landing page after channel creation
- Create the 'go' module to present several possible things to do after channel creation
- Add unit test for dba_pdo driver class
- Add unit test for \DBA factory
- Usability improvements to registration/verification workflow
- Don't do any bbcode translation within code blocks (except baseurl, observer, and linefeeds)
- Improve browser language detection
- Remove unused prototype importer template and obsolete reflection cms importer
- Update to bootstrap 4 stable
- Implement caching of notifications in browser session storage
- Code cleanup and simplification in mod_like
- Implement new cropper library
- Better notifications for edit post/comments which may have been originally posted long ago
- Ensure filter words are not empty in include/items.php
- Change query in mod search to be compatible with postgres
- Provide channel list function in the zot api
- Remove deprecated 'qcomment' feature
- Simplify webserver logic flow
- Simplify interactions with the get_features hook
- Provide a local pubstream option (content from this site only)
- Simplify dir_tagadelic dramatically
- Surface the article feature
- Add summary bbcode tag
- Move markdown-in-posts/comments feature to plugin
- Support tables in markdown posts/comments
Bugfixes
- Fix javascript error if there are no notifications
- Fix some issues with friend suggestions on standalone sites with no 'suggestme' volunteers
- Fix unable to reset profile fields to defaults in admin/profs by emptying the textarea
⁻ Fix issues with accordions related to bootstrap upgrade
- Fix empty dob is set to the date of the first profile save
- Fix several email validation issues
- Fix issue if logged in locally and mod_display returns nothing owned by your uid; retry with known public uids rather than issue 'permission denied'
- Fix public stream app permission check to match the recent fixes to the Module
- Fix issues with delivery of edited posts to forums
- Fix autoname test
- Fix issue where self and pending connections were visible in connections when not loged in
- Fix bad query in mod defperms
- Fix issue where gnusocial likes were not recognised as like activity
- Fix manual queue invocation
- Fix unable to delete accounts using tickboxes on admin/accounts
- Fix a PHP7.2 warning when a channel has no cards
- Fix unable to delete permission groups with space in name (github issue #920)
Addons
Statistic: fix reporting of incorrect register policy in nodeinfo
Diaspora: diaspora_init_relay: calls diaspora_import_author with too many arguments
Pubcrawl: provide a system 'allowed' for to match the system setting for other protocols
Diaspora: fix issue with sending diaspora profile change messages over diaspora_v2
Diaspora: provide limited but hopefully adequate support for new Diaspora html5 audio/video
Pubcrawl: send zot context with follow requests
Pubcrawl: add video to the set of message types we process
Pubcrawl: support for activitypub media
Openclipatar: remove extra details for each image
Diaspora: initial work on event participation
Statistic: remove the friendica protocol from nodeinfo until it is fully implemented
Statistic: re-arrange the order of the .host-meta/nodeinfo links
Pubcrawl: add share verb to activitystreams translator
Pubcrawl: post public posts to syschannel
Statistics: fix legacy statistics.json interface
Gnusocial: improve error checking when processing a salmon message
Dirstats: fix sql syntax error
Pubcrawl: possibly reduce constraint violations for xchan_store_lowlevel (duplicate entry)
Diaspora: ensure we process Friendica-over-Diaspora yearless birthdays correctly
Chess: added simple history browsing controls to spectator view
Diaspora: support post/comment edits
Diaspora: don't redirect fetch requests for non-Diaspora wall-to-wall and forum posts unless they can be redirected to a Diaspora protocol site
Chess: added support for publicly visible games
Phpmailer: add quickstart notes
Chess: choose random color if no color is chosen
New Plugin: mdpost - markdown in posts/comments, migrated from core to addon
Diaspora: provide a configuration option to import the diaspora firehose, otherwise only import content matching subscribed tags
NSFW: load images only after click on the button
Twitter: provide configurable tweet length until such time as 280 becomes universal
Hubzilla 3.0 (2018-01-09)
- Updated homeinstall script
- Sort cloud directory by 1. is_dir and 2. name
- Document that imagick calls/execs ffmpeg for mp4 video thumbnails
- Use pipe_stream() instead of file_{get, put}_contents() in attach_store()
- Make homeinstall script ready for Debian 9
- Add url and headings to bbco_autocomplete()
- Remove additional linebreaks after headings
- html2bbcode: use headings bbcode for headings
- Don't zidify all permalinks, only zot permalinks
- Make remote homelink link to the home host and not to the home channel
- Auto promote beginner (techlevel 0) accounts to level 1 after they show signs of active participation.
- Go back to including the photo thumbnail data in the export file.
- Improvements to file import/export
- Default value for xlink_rating_text
- Implement IMoveTarget and recursive file/directory move/rename - github issue #680
- Synchronise an attach_move operation to clones
- Provide a themed page with an error notification on errors instead of an obtuse XML error structure in mod cloud
- Disallow backslashes in wiki and wiki-page names
- We only require one update module. The rest are superfluous.
- Render installable elements as buttons instead of links
- Implement chunked uploads for photos page
- Remove warning for large files on cloud upload
- Add a filter for notification to show new posts only
- Implement chunked uploads for cloud
- Use httpsig auth for getfile
- Load the profile images in the custom acl selector only if we actually need them
- Rework liveUpdate() and notificationsUpdate() (aka ping) to first do the liveUpdate and when this is done only do the ping once.
- Don't include invisible "update activities" in category widget
- Default profile assign
- Provide system config option for minimum registration age.
- Remove deprecated $a argument from advanced_profile()
- Change to bbcode calling parameters
- Extra checking of server headers in upload functions
- Provide a handler for chunked uploads in mod file_upload
- Optional divider between item header and body
- Allow toggle to SMBC scaling mode.
- Add thumbnail hook
- Implement SVG thumbnails and expose security setting
- Implement video thumbnail generator
- Implement pdf thumbnails
- Implement thumbnail generator for epubs
- Make browser history buttons work with ajax calls in mod display and hq
- Implement tile view for mod cloud (read only)
- Add mp3 audio thumbnail generator
- Set display_path for photo_upload from the DAV File interface
- Provide a generalised interface for thumbnail generators to support various content types
- Add ID3Parser library.
- Text thumbnails in cloud tile mode
- Revisit media breakpoints - do not switch to mobile view to early.
- Add French to help pages language dropdown selector
- Inroduce the HQ module - an alternative landing page for hubzilla
- Strip author name from notify messages in notifications - github issue #911
- Remove column item.diaspora_meta
- Provide ability to pin apps to navbar from mod apps
- Add private forums to forum widget
- Move notifications style to widgets.css
- Sort out a few more large image upload issues
- Move notifications full-screen handling to notifications widget
- Move mailhost settings from plugin to core
- Sort combined private mail conversations by latest updated conversation instead of created parent
- Filter atokens on acl search
- Allow a site to block (public) the directory separately from other resources.
- Improve removed_channel final cleanup - github issue #386
- Cleanup of upload_to_comments(
- Dedicate the first click to slideup the cover again but make sure the nav buttons remain functional
- Set os_syspath in DAV file put operation so that photos will scale correctly.
- Unit tests for Zotlabs\Access classes
- Bring back tabindex to submit comments
- attach.php minor cleanup and doc
- Allow cloud filenames to include ampersands without messing up auth tokens (zid, owt, and zat, and the constant placeholder 'f=')
- Provide short localised summary for likes that will end up in displayed notifications
- Improving Doxygen documentation.
- Update item_normal() to not include ACTIVITY_OBJ_FILE obj_type
- Sort out issues with pubstream item interactions
- Don't perform zot_refresh on dead sites unless $force is set
- Do not send message_list responses to dead sites (this delivery method bypassed the notifier)
- Support for netselect query
- Add another delivery control parameter (queue threshold)
- Add some documentation about shareable widgets
- Allow plugin class widgets
- Some more work on unit tests
- Encrypt the owa token
- Bring back the markdown post feature
- We call Theme:url() statically, make it also static.
- Table structure for pseudo or proxy channels (pchan)
Bugfixes
- Fix sync non-default profile photo changes to clones - github issue #113
- Fix prev/next buttons on connedit can show deleted connections - github issue #673
- Fix affinity widget settings
- Fix dupe bug in content hooks - github issue #943
- Fix directory keywords returned from dir_tagadelic() in standalone mode
- Fix argument warning when arguments are correct in util/dcp
- Fix issue with long filenames in mod cloud
- Fix misc. issues with new 'insert photo from photo album' github issue #475
- Fix regression in channel sources delivery
- Fix loading of theme-specific widgets
- Fix unable to add wiki pages with spaces
- Fix mod display and others that require a non-zero profile_uid for updates
- Fix various PHP 7.2 issues
- Fix typo in HTTPSig
- Fix pagetitle lost importing a pdl element from conversation
- Fix js warning - getelementbyid (id doesn't exist)
- Fix some pubstream on/off weirdness
- Fix default addressbook has no name - github issue #921
- Fix double html ids in caldav widget if more than one sharee
- Fix regression in cdav calendar widget
- Fix sync packet not generated when deleting a file using the web browser interface
- Fix album cover thumb generator
- Fix like-button for images - github issue #826
- Fix typo - github issue #910
- Fix issue with group_rmv()
- Fix php warnings on photo delete
- Fix some conflicts between private tags and forum tags
- Fix some schema issues
- Fix wiki pages not updating after creating new page
- Fix a PHP warning in Permissions::FilledPerms()
- Fix unicode characters in urls tripping up url regexes - github issue #901
- Fix second half of github issue #893
- Fix common connections on suggestion page showing wildly different results than remote profile, and is consistently off by one
- Fix cloud redirects with owt tokens
- Fix issues with diaspora xchans
- Fix memory overflow trying to delete a connection with a very high noise to signal ratio
- Fix sql error in page module
- Fix unstar
Plugins/Addon
Diaspora: fix 'view full size' photo link - core github issue #947
Diaspora: implement recent changes in diaspora account_migration spec
GNU-Social: fix uploading a photo to a post results in double post - github issue 75
GNU-Social: fix gnusoc plugin not respecting delayed delivery - github issue 74
Pubcrawl: fix PHP warning
Diaspora: remove garbage from magic envelope
Diaspora: fix permalinks for zot reshares
New addon: hzfiles - sync files across hubzilla servers
Fix various PHP 7.2 issues
Remove Firefox social plugin - it was deprecated and removed in firefox version 57
Diaspora: unset id and parent for local comments
Pubsubhubbub: set interactive flag to avoid delivery killing if block_public is enabled
Mailhost addon moved to core
Remove js_upload addon
Hubzilla 2.8.1 (2017-11-11)
- Rename channel app events to calendar and add nav_set_selected() to /cal
- Load notifications links to /display via ajax if we are already in /display
- Add location info to the navbar for remote visitors
- Bring back tabindex to submit comments
- Add spanish translations for context help
- Added mode to portfolio widget
Bugfixes
- Fix os_syspath in DAV file put operation so that photos will scale correctly
- Fix unicode characters in urls tripping up url regexes - github issue #901
- Fix wiki pages not updating after creating new page
- Fix notifications covered by cover photo on medium size screens - github issue #906
- Fix unable to change permissions on wiki with space in name
- Fix only show nav app link if we have a selected app
- Fix unable to mark all messages read
- Fix imagedata not set correctly if large photo and imagick is not installed
- Fix issues with diaspora xchans
- Fix profile photo issue triggered by a previous bug
Plugins/Addon
N-S-F-W: improve the undocumented n-s-f-w author::word feature
Diaspora: update the import_diaspora tool for the version 2.0 account export files
Diaspora: fix comments are partly containing "diaspora_handle" instead of "author" - github issue #69
Pubcrawl: provide feature setting for downgrade_media option
Pubcrawl: fix issue where replies to replies did not find its parent
Diaspora: fix friendica likes on comments
Diaspora: fix private mail
Diaspora: fix third party deletes/retractions not propagating
Diaspora: likes not working - github issue #895 in core
Diaspora: fix comments from unknown persons not accepted if allow public comments is enabled - github issue #68
XMPP: fix php warning
Hubzilla 2.8 (2017-10-25)
- Redirect to be moderated items to /moderate
- Update notifications if notifications area remains open
- Create an actual logout module instead of relying on internal variables
- Add local_channel as a comanche condition variable
- Implement possibility to pin app-tray apps in the navbar via app category navbar_default
- Introduce custom navbars
- Re-implement single delivery
- Pdledit usability improvements
- Implement next generation notifications in right aside
- Implement single post view for /pubstream
- Make anonymous comments work in mod display
- Introduce notifications for unseen public stream posts (off by default)
- Preperatory work on Zot VI
- Add app for site admin
- Introduce experimental alternate channel_menu navigation (off by default)
- Introduce notifications for shared files
- Bring back notifications for account approvals
- Urlencode hashes from mod_acl
- Don't use chanlink_url() for feed mentions
- Design common friends widget to fit better in the app and move it to left aside
- Allow navbar to be used when cover photo is displayed in mod channel
- Implement admin setting to use imagick converter for large photos
- Process activity deletes from OStatus which for whatever reason do not use the industry standard tombstone mechanism
- Implement new css based spinner
- Move the link header initialisation from Router to Webserver
⁻ Extend activity_match() to work with arrays
- Updated the trusted CA cert database
- Ostatus - support likes of comments
- Provide ability to mention a forum by using !forumname as well as the traditional red style (@forumname+)
- Encrypt delivery reports (not backward compatible)
- Provide a space between link header params (draft-cavage-http-signatures-08)
- Turn common_friends into a widget
- Update to jquery-3.2.1
- Wiki pages sorted by name
- Create new hooks for permissions_accept and permissions_reject
- Provide rel=alternate link if no reshare content in post
- Add remote login button to login page
- DB update to add index to item.resource_id
- Implement wiki editing (name and acl)
- Provide a hook for importing a channel photo at channel creation time
- Implement wiki mimetype lock
- Bring back wiki downloads
- Add text/plain mimetype to wiki
- Implement per page mimetype selection for wikis
- Added english context help for apps and appman
- Implement owa (open web auth)
- Ignore diaspora_meta column on item import
- Check code permissions on cloud files
- Remove period from characters allowed in username
- Make comment highlighting more reliable
- Sign zot-info packets with httpsignatures
- Implement server to server magic auth
- Provide support for json-ld signatures
- Rewrite comment form open/close handling to be more reliable
- Radically reduce code duplication in updateConvItems()
- Remove discover tab in favour of the public stream app
- Apply autotime to all autotime classed elements when static loading a page
- Implement cards feature
- Extended support for help page translations including table of contents files at the top level
- Introduce util/dmkdir - a mkdir tool for DAV
- Various doco improvements
- Introduce util/dcp (DAV-copy) - copy file or directory from local system to Hubzilla
- Provide support for HTTPsig
- Implement mechanism for selective network following in protocol connectors (diaspora, ostatus, activitypub, zot, rss)
Bugfixes
- Fix w2w posts not removed in contact_remove() - github issue #837
- Fix guests not having a unique (non-existent) url
- Fix mod register re-using the password
- Fix write_storage permission not checked in /display
- Fix discovery of moderated items in enotify
- Fix profile thing image not deleted when thing deleted - github issue #868
- Fix deletions to comments not synced on wall posts
- Fix community tags not preserved on post edit - github issue #865
- Fix profile photo propagation issue if the local xchan_photo_[l|m|s] fields were changed from the /photo/profile/l/n form to photo/[hash] form by a clone operation
- Fix lockstate and current permissions not handed over to editor in mod card_edit
- Fix profile edit dropdown for multiple profiles
- Fix affinity slider spinner
- Fix mod pubsites broken
- Fix directory server admin selection includes known dead sites
- Fix sticky-kit issue where the bottom of left aside was not visible when section content was short
- Fix possibility to set bogus my_address
- Fix deleting of wiki pages
- Fix selected theme not appearing selected after change - github issue #855
- Fix an issue where some encoded mids were not found in /display
- Fix issue with mentions and xchans with @ or /
- Fix webfinger returns invalid XML - github issue #851
- Fix last remaining task in tasklist was not removed from view when completed
Plugins/Addon
Hubwall: Remove errant $1 string in sender name
Map federation protocols for zotinfo
Gnusoc: force ostatus profile photos to get refreshed monthly
Gnusoc: fix ostatus mention notifications
Gnusoc: unsubscribe to gnusoc feeds if connector is disabled
Phpmailer: not using load/unload
Gnusoc: don't provide some information if gnusoc is disabled by the channel
Diaspora: add a predelivery interval
Diaspora: support for likes on comments
Introduce the pubcrawl plugin - an unapologetically non-compliant ActivityPub Protocol implemention
Introduce gravatar plugin
Pubsubhubbub: produce much more compact PuSH feeds
Diaspora: support text comments on reshare posts
Diaspora: changes to delivery scenarios for the special handling of profile messages
Diaspora: put diaspora seed_location in json webfinger
Gnusoc: fix mis-attributed comments from mastodon
Gnusoc: allow discovery by url (not just reddress) and permit upgrade from 'unknown' network to gnusoc
Implement mechanism for selective network following in protocol connectors
Hubzilla 2.6.3 (2017-09-18)
- Fix anonymous comments/likes on photos - this is not yet implemented
- Fix favicon not displayed on certain pages
- Fix hubzilla logo icon for favicon and email notifications
- Fix an issue with displaying selected theme in settings/display
- [SECURITY] Restrict the input characters we accept in token verification strings to hex digits
- Remove hubzilla.nl from fallback directory servers
Hubzilla 2.6.2 (2017-08-31)
- Fix webfinger returns invalid XML (github issue #851)
Hubzilla 2.6.1 (2017-08-18)
- Fix a regression with dav clients
- Raise install requirements
Plugins/Addon
- Diaspora: fix PHP warning
- GNU-Social: fix PHP warning
Hubzilla 2.6 (2017-08-16)
- Upgrade to bootstrap-4 beta
- Consolidate disable_discover_tab config
- Fix some bbcode to markdown conversion issues
- Improved finding of recursive attachment permissions
- Smaller line-height for notification badges
- Bluegrid schema removed - will be added again if someone is willing to maintain it
- Improved file_activity()
- DB - add index for item.obj_type
- Add options flag to bb_to_markdown() so we can distinguish between diaspora use and other use and therefore filter and adjust content selectively
- Close the apps-menu if the notifications-menu is open and vice versa
- Remove redundant call to jquery ready function in photo albums view
- Remove borders from navbar toggler in mobile view
- Improve the formatting of shares when converting from bbcode to markdown
- Suppress fopen errors from dav
- Make local channel (not our own) nav menus appear similar to what we are used from remote channels
- Indicate the selected channel in the dropdown menu if the feature is enabled
- Provide a mechanism to mark apps active in the app tray
- Allow wildcard tag and category searches
- Improved installer
- Update some addon docs and ensure we only generate statistics once a day
- Turn url requests where argv[0] is something.xyz into module='something' and $_REQUEST['module_format'] = 'xyz'; But leave modules beginning with . (like .well_known) alone (convert the initial . to _ and then strip it)
- Turn platform name and std_version into config variables
- Implement chunked uploads on the wall
- Prevent expiration of conversations you are involved with
- Update htmlpurifier to version 4.9.3
- Update sabre/http to version 4.2.3
- Add optimize-autoloader to composer config
- Missing abook_{my,their}_perms in pg schema and missing keys in mysql schema
- Provide a gender icon on the profile sidebar within reason
- Provide more comprehensible information on the admin summary page
- Upgrade blueimp from 9.8 to 9.18
- Chanview - if already connected, bypass the chanview intermediary page and go straight to the remote profile.
- Allow poke by xchan_hash
- guess_image_type() - ignore scheme when checking for urls
- Remove unused page_widgets.php include and provide a general function for loading sql from file
- Migrate cdav from addons to core
- Address several mail issues
- Add files and photos to featured apps by default
- import_author_zot() fixes
- Remove deprecated app parameter from conversation()
- Implement anonymous comments (like wordpress)
- Add rel=noopener to all external target _blank links
- Add 'can_comment_on_post' hook so we can better deal with the complications of Diaspora policy
- Added Portfolio widget (requires foundation)
- Convert schema_mysql engine to InnoDB and charset utf8mb4
- Put unreachable federated connections in the archived tab of the connections list page
- Indicate on connections page if a federated connection from another network is unavailable from the current location
- Make authenticated oembeds optional, default to false.
- Remove text_highlight css load from core
- Numerous ostatus feed improvements (mastodon, gnu-social)
- Provide hook when deleting a connection - we need this to clean up dangling PuSH subscriptions
- Move code syntax highlighting to plugin
- Oembed: ensure that width and height are returned as type int and not float
- Rewrite wiki pages widget - no need for ajax on pageload, show the pages to not authenticated people.
- Convert randprof to use chanlink_hash() instead of chanlink_url() and filter sys channels by xchan.xchan_system instead of xchan_addr != sys@%
- Update Sabre libraries
- Only provide "connected apps" on the settings menu if techlevel > 0.
- Provide ability to search webpage
- Move disapora xrd stuff to plugin
- Deprecate server_role
- Introduce automatic language selection for help, webpages, and wiki content
- Provide ability to order apps in app-tray
- Replace Markdownify library with html-to-markdown library
Bugfixes
- Fix channel manager and nav channel select visible if in a delegate session
- Fix wrong wiki pages in the sidebar github issue #841
- Fix a bug where if multiple channels uploaded the same file to the same folder, the uploaded file would end up with an incremental number added to the filename for each upload even if the file did not exist yet in the channels folder
- Fix privacy groups not syncing across clones properly (github issue #832)
- Fix an issue where the ability to use a portion of the message-id to display a message wasn't honoured in all cases
- Fix minor issues in the bs-default schema
- Fix backward compatibility for album links generated in earlier times before the ambiguity of photo album names was solved (github issue #827)
- Fix photo item comments not ported to bs4
- Fix incorrect album link
- Fix incorrect follow url in webfinger
- Fix regression - allow position attributes in oembedable zcards
- Fix affinitiy slider settings were being updated on any submit of of settings/featured
- Fix minor weirdness in zot finger results after deleting a clone from a channel that was on a site which was previously migrated from http to https and still had the old hubloc
- Fix cloud headers already sent issue
- Partial fix for failure to sync photos - appears to be memory exhaustion and dependent on filesize although an unrelated issue was found with directory creation during file sync (we didn't check ownership when looking for duplicates)
- Fix github issue #810
- Don't allow negative age in directory listings
- Fix allow setting a default schema for the hub (github issue #797) and allow selecting of focus (hubzilla default) schema if a default is set
- Fix update_r1189() for mysql and postgres
Plugins/Addon
Diaspora: Rewrite the addon to implemented Diaspora Version 2 federation protocol
GNU-Social: GNU-Social and Mastodon compatibility was greatly increased and a "fetch conversations" feature added to try and locate missing contextual references and maintain conversations in posts from those networks
Rename statistics_json to statistics and implement nodeinfo v2
New authchoose addon to restrict what sites you authenticate to by default
Cdav addon moved to core
head_add_css() needs a preceding '/' to find files in the addons dir
New addon code syntax highlighting (moved from core to addon)
Pubsubhubbub: specify a minimum number of records - otherwise it defaults to zero
Hubzilla 2.4 (2017-05-31)
- Silence php warning during install
- Implemented switch statement logic in Comanche layout parser
- Don't allow html in plugin comment blocks
- Handle Mastodon urls in markdown/bbcode conversion
- Get rid of edit activities
- Collapse sysapps if viewing a remote channel
- Various Doxygen fixes
- Update SimplePie library to version 1.5
- Add check for PHP zip extension during install
- Add unit tests for AccessList class
- Authenticate onepoll so we can receive private posts/comments in zotfeed
- Various postgres fixes
- Some work on preparing clientside e2ee
- Allow to set a default channel for the rare case where a default channel is not selected but channels actually exist
- Support reverse magic-auth in oembed requests
- Improved handling of Mastodon feeds
- When template "none" is used in a webpage layout, then the contents of the page should be the sole output, with no other code before or after the page element content
- If there is no site record, site_dead won't be 0, in a left join it will in fact be null. As long as it isn't 1, we should attempt delivery
- Order wiki pages by creation date
- Backend infrastructure for channel protection password; which will be used to optionally encrypt export files and resolve channel/identity ownership/hijacking disputes
- Don't allow any null fields in notify creation
- Webfinger cleanup
- Envelope privacy
- We do not parse the body in discover_by_url(), so no need to preserve iframes in SimplePie
- Correct the mastodon "boost" (aka 'share') author attribution by checking for share activities and pulling the original author info from the activity:object
- Only log zot_refresh content if json decode was successful
- Revisit the import_author_zot algorithm yet again. There was one bug that we weren't returning necessary information in the first SQL query - and performance/loading problem if one tries to refresh a dead site
- Import_author_xchan - since we rarely refresh zot-info for non-connections, force a cache reload once a week to catch things like profile photo updates and location changes
- Create site_store_lowlevel() to initialise data structures for the site table
- Change hook for perm_is_allowed while retaining backwards compatibility
- import_author_zot() - check for both hubloc and xchan entries. This should catch and repair entries which were subject to transient storage failures
- Import authors from any unrecognised network as network 'unknown'
- Crypto update - default is now aes-256-ctr
- Get rid of get_app()
- Add 'author_is_pmable()' function with plugin hooks to control whether or not to display a 'send mail' link in the thread author menu
- Provide platform specific install script
- Allow for project specific DB updates
- Get rid of davguest
- Move db_upgrade to zlib
- Add CSRF protection for import and import_items
- Add some documentation for import functions
- Do not allow creating two wikis with the same name
- Update textcomplete library to version 1.8.0
- Create channel_store_lowlevel()
- Allow setting the system email name/address/reply
- Use the same host macro for sender address as for reply_to address
- Use the relevant attach directory/path for photo albums instead of an album basename which may not be unique. Created an 'ellipsify()' function to shorten long names and keep the beginning and end intact
- Simplify the message signing spaghetti
- Class MarkdownSoap to safely store markdown by purifying and preserving (escaped) what may be unsafe code in codeblocks. The stored item needs to be unescaped just prior to calling the markdown-to-html processor
- Remove the unimplemented upload limit site settings from UI
- Cleanup code_allowed
- Move widgets to standalone classes
- Upgrade redbasic to bootstrap 4
- Updated HTML Purifier from 4.6.0 to 4.9.2 with better PHP7 compatibility
- Remove redundant and non-functional/broken check for successfully cloned channel record which was left over from an earlier method of creating the table; which was deprecated a few months back
- Update bshaffer/oauth2-server-php library
- Add unit test for purify_html()
Bugfixes
- Fix website export tool creating invalid zip file - issue #790
- Fix files not synced correctly - issue #769
- Fix empty ACL should not result in no ACL when uploading a file
- Fix cover photo was unintentionally disabled when block_public in effect
- Fix markdown autolinks - issue 752
- Fix connectDefaultShare generated js function, though it isn't obvious if we still use it
- Fix a couple more instances where we were still calling mail() directly for site critical messages
- Fix when clicking a notification to view a private mail message, actually view that message instead of the most recent
- Fix group by item query
Plugins/Addon
- smileybutton: do not load emojis
- pubsubhubbub: fixes associated with recent compatibility feed mods
- gnusoc: mastodon follow_activity compatibility issues
- gnusoc: add profile photo to feed meta
- gnusoc: add salmon link information to the public feed when GNU-Social is enabled
- chess: fix bugs when deleting games
Hubzilla 2.2 (2017-03-08)
- Provide version compatibility check for themes (minversion, maxversion)
- Use chanlink_hash() instead of chanlink_url() where appropriate
- Use head_add_link() for feed discovery
- Provide HTTP header parser which honours continuation lines
- Numerous doco improvements
- Implement virtual privacy groups from restricted profile access list
- Implement permission roles
- Implement app-tray
- Default to manual conversation updates
- Implement channel move for all server roles
- Implement nav login modal
- Rename bb2diaspora.php to markdown.php
- Remove obsolete module 'match'
- Move firefox social api configuration to plugin
- Move rsd service to twitter_api plugin
- Add build_pagehead hook
- Move opensearch to plugins
- Move dreamhost hack to plugin
- Add wiki permissions
- Introduce hubloc_store_lowlevel() and xchan_store_lowlevel()
- Move diaspora account import to the diaspora plugin
- Allow export of single data sets instead of always exporting everything we know about in channel export
- Queue optimisations for sites that have lingered in the queue for more than a couple of days
- Add affinity slider tool settings for min and max defaults in settings/featured
- Provide lowlevel xchan storage function to ensure that all non-null rows are initialised
- Implement native wiki
- Block well-known from oembed
- Implement observer.language bbcode and observer.language comanche conditional
- Implement daemon_addon hook to let plugins create custom background processes
- Implement profile vcards
- Implement connection vcards
- Implement 'click to call' in address book
- Default cover photo
- Remove fullscreen functionality in photo album view
- Update fontawesome lib to version 4.7.0
- Implement a menu to select a section to be open by default in connedit
- Improve comanche conditionals
- Add enclosures and categories to atom feed parsing
- Allow the atom_entry hook to change the results
- Set 'adjust for viewer timezone' as the default for new events
- Allow event creation in other timezones than your own
- Update fullcalendar lib to version 3.1
- Move api version call back to core
- Create first webpage as 'home' if none exist
- Show webpages link to visitors if a 'home' page exists
Bugfixes
- Fix schema not saved if session theme != selected theme and schema select display issue
- Fix no acl not detected in post_activity_item()
- Fix find_folder_hash_by_path() was not safe against multiple attach structures with the same filename but in different directories
- Fix don't search on empty filename - we shouldn't find it. The reason why this change is being made is because we actually did find it due to a development glitch
- Fix several places where head_add_(css|js) functions have been used incorrectly.
- Fix webpage import tool
- Fix numerous bugs with the addon repo management GUI
- Fix attach_delete() to remove photo resources even if the attach table row wasn't found
- Fix choking if photo_factory() returns null
- Fix embedimage if an albumname contains quotes
- Fix chat member list when one or more members are connected via access tokens
- Fix issue #636 - some localised (e.g. Italian) strings have single quotes which throw JS errors when used in single quoted template constructs
- Fix issues #629 and #635 - edited post arriving from downstream source was not being rejected
- Fix peoplefind widget not honouring directory option settings
- Fix issue with HTML in code blocks in markdown in wiki
- Fix issue with post signatures if posted from api and logged in locally with a different identity
Plugins/Addon
- Add experimental webmention plugin
- NSFW: Use button instead of text link
- Diaspora: gracefully handle multiple photos per post
- Diaspora: change profile photo permission call
- Logrotate: don't throw an error if another server process renamed the logfile before we got to it
- Chess: the channel owner must be one of the players, so only require selecting one connection for an opponent
- Move firefox social api configuration to plugin from core
- Move rsd service to twitter_api plugin from core
- Move opensearch to plugins from core
- Move dreamhost hack to plugin from
- Move diaspora account import to addon from core
- Reflect hubloc store changes in plugins
- Reflect xchan store changes in plugins
- Rendezvous: Fixed marker creation bug
- Rendezvous: Center on marker if specified in URL, and update browser address bar with shareable link when selecting markers on the map
- Rendezvous: Set default value of 0 for priximity alert when making new markers
- Move gitwiki to plugins from core which has been replaced by native wiki
- Openclipatar: reflect changes to files and photos which were unified in core some time ago
- Reintroduce gnusocial plugin after security/functionality review
- Twitter_api: hubzilla core issue 638 - unsupported message-id field not available in all twitter api functions
- Superblock: update to reflect core changes
- Rendezvous: implement static marker proximity alert
- Phpmailer: security update
Hubzilla 2.0 (2016-12-23)
- Deprecate bb_iframe
- Note widget: resize the textarea to reveal full content
- Implement fixed left aside
- Implement lockview for wikilist
- Simplify wikilist widget
- Router error reporting
- Setup changes to check for shell_exec and exec functions
- Extensible permissions upgrade handling for channels with custom permission roles
- Allow plugins to cancel item_store() and item_store_update()
- ZOT version 1.2 provides negotiation of cryptographic algorithms
- Provide a fresh new look and cleaner layout and more relevant information to siteinfo
- Introduce highlight bbcode [hl]
- Implement wiki mimetypes markdown or bbcode
- Doc pages refactoring
- Update webpages and wiki context help
- Make a git commit when a new wiki page is created
- Prev-next navigation for mod_connedit to ease bulk connection edits
- Move the remote user homebutton to the user menu
- Do not render maps/locations for Diaspora destinations
- Provide 'per-page' caching for is_matrix_url() results to reduce duplicate queries
- Don't send notification for posts/comments on old conversations that were refetched after having expired
- Numerous wiki UI improvements
- Move twitter api to addon
- Cleanup and re-organise the voting and attendance buttons
- Reorganise emoticons
- Collapse navbar-collapse-1 if avatar menu is clicked.
- New display setting: static page update as opposed to live update
- Command line administrative channel connect utility
- Modernise chanview
- Implement edit activities to share post/comment edits with protocols which do not support them (e.g. Diaspora)
- Wiki export
- Numerous postgres compatibility fixes
- Remove requirement that imported profile photos be in the profile photos album
- Change event behaviour - share by default.
- Use PDO database driver exclusively (deprecate drivers that are separately maintained)
- Zot API re-write and extended
Bugfixes
- Fix z_fetch_url() incorrect variable
- Fix SQL error with app categories
- Fix do not show revert buttons if we do not have write perms
- Fix dropdown positions
- Fix do not increase opacity to more than 1
- Fix clone sync missing for some item delete operations
- Fix embed-image for fullscreen mode
- Fix attach_list_files()
- Fix full screen for embedded videos
- Fix the forum widget for forums with custom perms
- Fix issue #607 parens not recognised inside urls
- Fix pubsites: don't list dead sites
- Fix issue #596 silence headers already sent warning
- Fix missing plugins in zot-info
- Fix notification issue
- Fix issue #594 like of thing appears as profile owner like
- Fix export issue
- Fix checklist bbcode - only turn [] and [x] into checkboxes if it is found inside a checklist
- Fix wiki permissions issues
- Fix public calendar leaks connection information (birthdays) when view_contacts is not allowed
- Fix attach_rename: flaw in duplicate filename detection resulted in filename(1)(1)(1).ext
- Fix a fatal error with incorrect DB object access
- Provide /locs link on settings page if there is more than one hubloc for this channnel *that isn't deleted*.
- Fix issue #577 if connecting to a channel that is already pending, undo the pending and set connect permissions accordingly
- Fix issue #575, when 'nofinish' is set on an event, invalid date was generated/stored
- Fix bbcode event formatting issue
- Fix zot_finger from navbar people search looping
- Fix fromStandalonePermission()
Plugins
- GNU Social: removed from addons for security reasons - it might be re-implemented once it is properly reviewed
- Diaspora: missing item author when diaspora public comment received from relay
- Superblock: refactoring
- New addon: tripleaes for pro
- Cdav: "if not exists" only supported starting with postgresql v. 9.5 debian stable has 9.4
- Rendezvous: added markers and members export tool at /rendezvous/[group_id]/export/{markers,members}
- Twitter: move twitter api to addon
- New addon: b2tbtn (back to top button)
- Diaspora: import public diaspora messages to sys if applicable
- Diaspora: try and handle singletons better and simplify the associated notifier decisions
- Rendezvous: add proximity alert feature to members to issue notification when member is within a specified distance.
- New addon: diaspora_reconnect to refriend diaspora/friendica connections from a clone or channel move
- Diaspora: change the logic for deciding between upstream and downstream message flow for notifier plugins
- Rendezvous: prompt member to share their location by activating the GPS control using a tooltip and pulsing visibility
- statistics_json: fix nodeinfo
- Rendezvous: restored the lost gps-icon.png and corrected the OpenStreetMap tile server URL to avoid insecure content warnings
- Rendezvous: use observer name if available
- std_embeds: missing backslash
- Diaspora: postgres fixes issue #31
- Rendezvous: added marker list with centering buttons and popup open.
- Rendezvous: added control to see list of members sharing their location, with buttons to pan the map to center them
- Diaspora: system level diaspora toggle
- Rendezvous: added control that displays members.
- Diaspora: rename diaspora2bb() to markdown_to_bb() in core
- Hubwall: remove illegal unescaped angle chars
- Rendezvous: Add control to delete member if not updated in over 14 minutes
Hubzilla 1.14 (2016-10-13)
- New hook bbcode_filter
- Unify the various mail sending instance to enotify::send() and z_mail()

View File

@@ -1,37 +0,0 @@
Developer Certificate of Origin
Version 1.1
Copyright (C) 2004, 2006 The Linux Foundation and its contributors.
1 Letterman Drive
Suite D4700
San Francisco, CA, 94129
Everyone is permitted to copy and distribute verbatim copies of this
license document, but changing it is not allowed.
Developer's Certificate of Origin 1.1
By making a contribution to this project, I certify that:
(a) The contribution was created in whole or in part by me and I
have the right to submit it under the open source license
indicated in the file; or
(b) The contribution is based upon previous work that, to the best
of my knowledge, is covered under an appropriate open source
license and I have the right under that license to submit that
work with modifications, whether created in whole or in part
by me, under the same open source license (unless I am
permitted to submit under a different license), as indicated
in the file; or
(c) The contribution was provided directly to me by some other
person who certified (a), (b) or (c) and I have not modified
it.
(d) I understand and agree that this project and the contribution
are public and that a record of the contribution (including all
personal information I submit with it, including my sign-off) is
maintained indefinitely and may be redistributed consistent with
this project or the open source license(s) involved.

View File

@@ -1,4 +1,4 @@
Copyright (c) 2010-2018 the Hubzilla Community
Copyright (c) 2010-2016 the Hubzilla Community
All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy

View File

@@ -3,27 +3,60 @@
Hubzilla - Community Server
===========================
Groupware re-imagined and re-invented.
--------------------------------------
Connect and link decentralised web communities.
-----------------------------------------------
<p align="center" markdown="1">
<em><a href="https://framagit.org/hubzilla/core/blob/master/install/INSTALL.txt">Installing Hubzilla</a></em>
<em><a href="https://github.com/redmatrix/hubzilla/blob/master/install/INSTALL.txt">Installing Hubzilla</a></em>
</p>
**What is Hubzilla?**
**What are Hubz?**
Hubzilla is a general purpose communication server integrated with a web publishing system and a decentralised permission system. If this sounds like a bunch of technical mumbo-jumbo to you, just think of it as an independent platform for sharing stuff online (publicly or privately).
Hubz are independent general-purpose websites that not only connect with their associated members and viewers, but also connect together to exchange personal communications and other information with each other.
This allows hub members on any hub to securely and privately share anything; with anybody, on any hub - anywhere; or share stuff publicly with anybody on the internet if desired.
Hubzilla contains some social network bits, some cloud storage bits, some blog and forum bits, and some content management bits. These are all integrated within a common privacy framework - and it is all decentralised.
**Hubzilla** is the server software which makes this possible. It is a sophisticated and unique combination of an open source content management system and a decentralised identity, communications, and permissions framework and protocol suite, built using common webserver technology (PHP/MySQL/Apache and popular variants). The end result is a level of systems integration, privacy control, and communications features that you wouldn't think are possible in either a content management system or a decentralised communications network. It also brings a new level of cooperation and privacy to the web and introduces the concept of personally owned "single sign-on" to web services across the entire internet.
Everything you publish or share can be restricted to those channels and people you wish to share them with; and these permissions work completely invisibly - **even with channels on different servers or other communications services**.
Hubzilla hubz are
Migration and live backups of your connections, settings, and everything you publish are built-in, so you never need worry about server failure.
* decentralised
* inherently social
* optionally inter-networked with other hubz
* privacy-enabled (privacy exclusions work across the entire internet to any registered identity on any compatible hubz)
Hubzilla is completely decentralised and open source, for you modify or adapt to your needs and desires. Plugins, themes, and numerous configuration options extend the overall capabilities to do anything you can imagine.
Possible website applications include
* decentralised social networking nodes
* personal cloud storage
* file dropboxes
* managing organisational communications and activities
* collaboration and community decision-making
* small business websites
* public and private media/file libraries
* blogs
* event promotion
* feed aggregation and republishing
* forums
* dating websites
* pretty much anything you can do on a traditional blog or community website, but that you could do better if you could easily connect it with other websites or privately share things across website boundaries.
**Who Are We?**
<p align="center" markdown="1">
<em><a href="https://github.com/redmatrix/hubzilla/blob/master/install/INSTALL.txt">Installing Hubzilla</a></em>
</p>
**Who Are We and What Are Our Principles?**
The Hubzilla community is powered by passionate volunteers creating an open source **commons** of decentralised services which are highly integrated and can rival the feature set of centralised providers. We are open to sponsorship and donations to cover expenses and compensate for our time and energy, however the project core is basically non-profit and is not designed for the purpose of commercial gain or exploitation.
Some sites may include monetisation strategies such as subscriptions and *freemium* models where members pay for resources they consume beyond a basic level. The project community supports such monetisation initiatives (nobody should be forced to pay "out of pocket" to provide a service to others), but we maintain the **commons** to provide open and free access of the software to all.
The software is not designed for data collection of its members or providing advertising. We don't have a need or desire for these things and feel that software built around these goals is poorly designed and represents compromised principles and ethics.
As a project, we are inclusive of all beliefs and cultures and do what we are able to provide an environment that is free from hostility and harrassment. Whether or not we succeed in this endaevour requires constant vigilance and help from all members of the community, working together to build an inter-networking tool with amazing potential.
The Hubzilla community consists of passionate volunteers creating an open source commons of decentralised services which are highly integrated and can rival the feature set of large centralised providers. We do our best to provide ethical software which places you in control of your online communications and privacy expectations.
[![Build Status](https://travis-ci.org/redmatrix/hubzilla.svg)](https://travis-ci.org/redmatrix/hubzilla)

View File

@@ -2,59 +2,21 @@
namespace Zotlabs\Access;
/**
* @brief AccessList class which represents individual content ACLs.
*
* A class to hold an AccessList object with allowed and denied contacts and
* groups.
*
* After evaluating @ref ::Zotlabs::Access::PermissionLimits "PermissionLimits"
* and @ref ::Zotlabs::Lib::Permcat "Permcat"s individual content ACLs are evaluated.
* These answer the question "Can Joe view *this* album/photo?".
*/
class AccessList {
/**
* @brief Allow contacts
* @var string
*/
private $allow_cid;
/**
* @brief Allow groups
* @var string
*/
private $allow_gid;
/**
* @brief Deny contacts
* @var string
*/
private $deny_cid;
/**
* @brief Deny groups
* @var string
*/
private $deny_gid;
/**
* @brief Indicates if we are using the default constructor values or
* values that have been set explicitly.
* @var boolean
*/
private $explicit;
/* indicates if we are using the default constructor values or values that have been set explicitly. */
private $explicit;
/**
* @brief Constructor for AccessList class.
*
* @note The array to pass to the constructor is different from the array
* that you provide to the set() or set_from_array() functions.
*
* @param array $channel A channel array, where these entries are evaluated:
* * \e string \b channel_allow_cid => string of allowed cids
* * \e string \b channel_allow_gid => string of allowed gids
* * \e string \b channel_deny_cid => string of denied cids
* * \e string \b channel_deny_gid => string of denied gids
*/
function __construct($channel) {
if($channel) {
if($channel) {
$this->allow_cid = $channel['channel_allow_cid'];
$this->allow_gid = $channel['channel_allow_gid'];
$this->deny_cid = $channel['channel_deny_cid'];
@@ -70,95 +32,61 @@ class AccessList {
$this->explicit = false;
}
/**
* @brief Get if we are using the default constructor values
* or values that have been set explicitly.
*
* @return boolean
*/
function get_explicit() {
return $this->explicit;
}
/**
* @brief Set access list from strings such as those in already
* existing stored data items.
*
* @note The array to pass to this set function is different from the array
* that you provide to the constructor or set_from_array().
*
* @param array $arr
* * \e string \b allow_cid => string of allowed cids
* * \e string \b allow_gid => string of allowed gids
* * \e string \b deny_cid => string of denied cids
* * \e string \b deny_gid => string of denied gids
* @param boolean $explicit (optional) default true
* Set AccessList from strings such as those in already
* existing stored data items
*/
function set($arr, $explicit = true) {
function set($arr,$explicit = true) {
$this->allow_cid = $arr['allow_cid'];
$this->allow_gid = $arr['allow_gid'];
$this->deny_cid = $arr['deny_cid'];
$this->deny_gid = $arr['deny_gid'];
$this->explicit = $explicit;
}
/**
* @brief Return an array consisting of the current access list components
* where the elements are directly storable.
*
* @return array An associative array with:
* * \e string \b allow_cid => string of allowed cids
* * \e string \b allow_gid => string of allowed gids
* * \e string \b deny_cid => string of denied cids
* * \e string \b deny_gid => string of denied gids
*/
function get() {
return [
'allow_cid' => $this->allow_cid,
'allow_gid' => $this->allow_gid,
'deny_cid' => $this->deny_cid,
'deny_gid' => $this->deny_gid,
];
}
/**
* @brief Set access list components from arrays, such as those provided by
* acl_selector().
*
* For convenience, a string (or non-array) input is assumed to be a
* comma-separated list and auto-converted into an array.
*
* @note The array to pass to this set function is different from the array
* that you provide to the constructor or set().
*
* @param array $arr An associative array with:
* * \e array|string \b contact_allow => array with cids or comma-seperated string
* * \e array|string \b group_allow => array with gids or comma-seperated string
* * \e array|string \b contact_deny => array with cids or comma-seperated string
* * \e array|string \b group_deny => array with gids or comma-seperated string
* @param boolean $explicit (optional) default true
*/
function set_from_array($arr, $explicit = true) {
$this->allow_cid = perms2str((is_array($arr['contact_allow']))
? $arr['contact_allow'] : explode(',', $arr['contact_allow']));
$this->allow_gid = perms2str((is_array($arr['group_allow']))
? $arr['group_allow'] : explode(',', $arr['group_allow']));
$this->deny_cid = perms2str((is_array($arr['contact_deny']))
? $arr['contact_deny'] : explode(',', $arr['contact_deny']));
$this->deny_gid = perms2str((is_array($arr['group_deny']))
? $arr['group_deny'] : explode(',', $arr['group_deny']));
$this->explicit = $explicit;
}
/**
* @brief Returns true if any access lists component is set.
*
* @return boolean Return true if any of allow_* deny_* values is set.
* return an array consisting of the current
* access list components where the elements
* are directly storable.
*/
function get() {
return array(
'allow_cid' => $this->allow_cid,
'allow_gid' => $this->allow_gid,
'deny_cid' => $this->deny_cid,
'deny_gid' => $this->deny_gid,
);
}
/**
* Set AccessList from arrays, such as those provided by
* acl_selector(). For convenience, a string (or non-array) input is
* assumed to be a comma-separated list and auto-converted into an array.
*/
function set_from_array($arr,$explicit = true) {
$this->allow_cid = perms2str((is_array($arr['contact_allow']))
? $arr['contact_allow'] : explode(',',$arr['contact_allow']));
$this->allow_gid = perms2str((is_array($arr['group_allow']))
? $arr['group_allow'] : explode(',',$arr['group_allow']));
$this->deny_cid = perms2str((is_array($arr['contact_deny']))
? $arr['contact_deny'] : explode(',',$arr['contact_deny']));
$this->deny_gid = perms2str((is_array($arr['group_deny']))
? $arr['group_deny'] : explode(',',$arr['group_deny']));
$this->explicit = $explicit;
}
function is_private() {
return (($this->allow_cid || $this->allow_gid || $this->deny_cid || $this->deny_gid) ? true : false);
}
}

View File

@@ -2,90 +2,35 @@
namespace Zotlabs\Access;
use Zotlabs\Lib\PConfig;
use \Zotlabs\Lib as ZLib;
/**
* @brief Permission limits.
*
* Permission limits are a very high level permission setting. They are hard
* limits by design.
* "Who can view my photos (at all)?"
* "Who can post photos in my albums (at all)?"
*
* For viewing permissions we generally set these to 'anybody' and for write
* permissions we generally set them to 'those I allow', though many people
* restrict the viewing permissions further for things like 'Can view my connections'.
*
* People get confused enough by permissions that we wanted a place to set their
* privacy expectations once and be done with it.
*
* Connection related permissions like "Can Joe view my photos?" are handled by
* @ref ::Zotlabs::Lib::Permcat "Permcat" and inherit from the channel's Permission
* limits.
*
* @see Permissions
*/
class PermissionLimits {
/**
* @brief Get standard permission limits.
*
* Viewing permissions and post_comments permission are set to 'anybody',
* other permissions are set to 'those I allow'.
*
* The list of permissions comes from Permissions::Perms().
*
* @return array
*/
static public function Std_Limits() {
$limits = [];
$perms = Permissions::Perms();
$limits = array();
foreach($perms as $k => $v) {
if(strstr($k, 'view'))
if(strstr($k,'view'))
$limits[$k] = PERMS_PUBLIC;
else
$limits[$k] = PERMS_SPECIFIC;
}
return $limits;
}
/**
* @brief Sets a permission limit for a channel.
*
* @param int $channel_id
* @param string $perm
* @param int $perm_limit one of PERMS_* constants
*/
static public function Set($channel_id, $perm, $perm_limit) {
PConfig::Set($channel_id, 'perm_limits', $perm, $perm_limit);
static public function Set($channel_id,$perm,$perm_limit) {
ZLib\PConfig::Set($channel_id,'perm_limits',$perm,$perm_limit);
}
/**
* @brief Get a channel's permission limits.
*
* Return a channel's permission limits from PConfig. If $perm is set just
* return this permission limit, if not set, return an array with all
* permission limits.
*
* @param int $channel_id
* @param string $perm (optional)
* @return
* * \b false if no perm_limits set for this channel
* * \b int if $perm is set, return one of PERMS_* constants for this permission, default 0
* * \b array with all permission limits, if $perm is not set
*/
static public function Get($channel_id, $perm = '') {
static public function Get($channel_id,$perm = '') {
if($perm) {
return intval(PConfig::Get($channel_id, 'perm_limits', $perm));
return Zlib\PConfig::Get($channel_id,'perm_limits',$perm);
}
PConfig::Load($channel_id);
if(array_key_exists($channel_id, \App::$config)
&& array_key_exists('perm_limits', \App::$config[$channel_id]))
return \App::$config[$channel_id]['perm_limits'];
return false;
}
else {
Zlib\PConfig::Load($channel_id);
if(array_key_exists($channel_id,\App::$config) && array_key_exists('perm_limits',\App::$config[$channel_id]))
return \App::$config[$channel_id]['perm_limits'];
return false;
}
}
}

View File

@@ -1,24 +1,12 @@
<?php
namespace Zotlabs\Access;
/**
* @brief PermissionRoles class.
*
* @see Permissions
*/
use Zotlabs\Lib as Zlib;
class PermissionRoles {
/**
* @brief PermissionRoles version.
*
* This must match the version in Permissions.php before permission updates can run.
*
* @return number
*/
static public function version() {
return 2;
}
static function role_perms($role) {
@@ -32,43 +20,24 @@ class PermissionRoles {
$ret['default_collection'] = false;
$ret['directory_publish'] = true;
$ret['online'] = true;
$ret['perms_connect'] = [
$ret['perms_connect'] = [
'view_stream', 'view_profile', 'view_contacts', 'view_storage',
'view_pages', 'view_wiki', 'send_stream', 'post_wall', 'post_comments',
'post_mail', 'chat', 'post_like', 'republish'
];
'view_pages', 'send_stream', 'post_wall', 'post_comments',
'post_mail', 'chat', 'post_like', 'republish' ];
$ret['limits'] = PermissionLimits::Std_Limits();
break;
case 'social_federation':
$ret['perms_auto'] = false;
$ret['default_collection'] = false;
$ret['directory_publish'] = true;
$ret['online'] = true;
$ret['perms_connect'] = [
'view_stream', 'view_profile', 'view_contacts', 'view_storage',
'view_pages', 'view_wiki', 'send_stream', 'post_wall', 'post_comments',
'post_mail', 'chat', 'post_like', 'republish'
];
$ret['limits'] = PermissionLimits::Std_Limits();
$ret['limits']['post_comments'] = PERMS_AUTHED;
$ret['limits']['post_mail'] = PERMS_AUTHED;
$ret['limits']['post_like'] = PERMS_AUTHED;
$ret['limits']['chat'] = PERMS_AUTHED;
break;
case 'social_restricted':
$ret['perms_auto'] = false;
$ret['default_collection'] = true;
$ret['directory_publish'] = true;
$ret['online'] = true;
$ret['perms_connect'] = [
$ret['perms_connect'] = [
'view_stream', 'view_profile', 'view_contacts', 'view_storage',
'view_pages', 'view_wiki', 'send_stream', 'post_wall', 'post_comments',
'post_mail', 'chat', 'post_like'
];
'view_pages', 'send_stream', 'post_wall', 'post_comments',
'post_mail', 'chat', 'post_like' ];
$ret['limits'] = PermissionLimits::Std_Limits();
break;
@@ -78,11 +47,10 @@ class PermissionRoles {
$ret['default_collection'] = true;
$ret['directory_publish'] = false;
$ret['online'] = false;
$ret['perms_connect'] = [
$ret['perms_connect'] = [
'view_stream', 'view_profile', 'view_contacts', 'view_storage',
'view_pages', 'view_wiki', 'send_stream', 'post_wall', 'post_comments',
'post_mail', 'post_like'
];
'view_pages', 'send_stream', 'post_wall', 'post_comments',
'post_mail', 'post_like' ];
$ret['limits'] = PermissionLimits::Std_Limits();
$ret['limits']['view_contacts'] = PERMS_SPECIFIC;
$ret['limits']['view_storage'] = PERMS_SPECIFIC;
@@ -94,13 +62,12 @@ class PermissionRoles {
$ret['default_collection'] = false;
$ret['directory_publish'] = true;
$ret['online'] = false;
$ret['perms_connect'] = [
$ret['perms_connect'] = [
'view_stream', 'view_profile', 'view_contacts', 'view_storage',
'view_pages', 'view_wiki', 'post_wall', 'post_comments', 'tag_deliver',
'post_mail', 'post_like' , 'republish', 'chat'
];
$ret['limits'] = PermissionLimits::Std_Limits();
'view_pages', 'post_wall', 'post_comments', 'tag_deliver',
'post_mail', 'post_like' , 'republish', 'chat' ];
$ret['limits'] = PermissionLimits::Std_Limits();
break;
case 'forum_restricted':
@@ -108,10 +75,11 @@ class PermissionRoles {
$ret['default_collection'] = true;
$ret['directory_publish'] = true;
$ret['online'] = false;
$ret['perms_connect'] = [
$ret['perms_connect'] = [
'view_stream', 'view_profile', 'view_contacts', 'view_storage',
'view_pages', 'view_wiki', 'post_wall', 'post_comments', 'tag_deliver',
'view_pages', 'post_wall', 'post_comments', 'tag_deliver',
'post_mail', 'post_like' , 'chat' ];
$ret['limits'] = PermissionLimits::Std_Limits();
break;
@@ -121,17 +89,17 @@ class PermissionRoles {
$ret['default_collection'] = true;
$ret['directory_publish'] = false;
$ret['online'] = false;
$ret['perms_connect'] = [
$ret['perms_connect'] = [
'view_stream', 'view_profile', 'view_contacts', 'view_storage',
'view_pages', 'view_wiki', 'post_wall', 'post_comments',
'post_mail', 'post_like' , 'chat'
];
'view_pages', 'post_wall', 'post_comments',
'post_mail', 'post_like' , 'chat' ];
$ret['limits'] = PermissionLimits::Std_Limits();
$ret['limits']['view_profile'] = PERMS_SPECIFIC;
$ret['limits']['view_contacts'] = PERMS_SPECIFIC;
$ret['limits']['view_storage'] = PERMS_SPECIFIC;
$ret['limits']['view_pages'] = PERMS_SPECIFIC;
$ret['limits']['view_wiki'] = PERMS_SPECIFIC;
break;
@@ -140,11 +108,12 @@ class PermissionRoles {
$ret['default_collection'] = false;
$ret['directory_publish'] = true;
$ret['online'] = false;
$ret['perms_connect'] = [
$ret['perms_connect'] = [
'view_stream', 'view_profile', 'view_contacts', 'view_storage',
'view_pages', 'view_wiki', 'send_stream', 'post_wall', 'post_comments',
'post_mail', 'post_like' , 'republish'
];
'view_pages', 'send_stream', 'post_wall', 'post_comments',
'post_mail', 'post_like' , 'republish' ];
$ret['limits'] = PermissionLimits::Std_Limits();
break;
@@ -154,11 +123,11 @@ class PermissionRoles {
$ret['default_collection'] = true;
$ret['directory_publish'] = false;
$ret['online'] = false;
$ret['perms_connect'] = [
$ret['perms_connect'] = [
'view_stream', 'view_profile', 'view_contacts', 'view_storage',
'view_pages', 'view_wiki', 'send_stream', 'post_wall', 'post_comments',
'post_mail', 'post_like' , 'republish'
];
'view_pages', 'send_stream', 'post_wall', 'post_comments',
'post_mail', 'post_like' , 'republish' ];
$ret['limits'] = PermissionLimits::Std_Limits();
break;
@@ -168,10 +137,11 @@ class PermissionRoles {
$ret['default_collection'] = false;
$ret['directory_publish'] = true;
$ret['online'] = false;
$ret['perms_connect'] = [
$ret['perms_connect'] = [
'view_stream', 'view_profile', 'view_contacts', 'view_storage',
'view_pages', 'view_wiki', 'post_like' , 'republish'
];
'view_pages', 'post_like' , 'republish' ];
$ret['limits'] = PermissionLimits::Std_Limits();
break;
@@ -181,136 +151,65 @@ class PermissionRoles {
$ret['default_collection'] = false;
$ret['directory_publish'] = true;
$ret['online'] = false;
$ret['perms_connect'] = [
'view_stream', 'view_profile', 'view_contacts', 'view_storage',
'view_pages', 'view_wiki', 'write_storage', 'write_pages', 'post_wall', 'post_comments', 'tag_deliver',
'post_mail', 'post_like' , 'republish', 'chat', 'write_wiki'
];
$ret['limits'] = PermissionLimits::Std_Limits();
$ret['perms_connect'] = [
'view_stream', 'view_profile', 'view_contacts', 'view_storage',
'view_pages', 'write_storage', 'write_pages', 'post_wall', 'post_comments', 'tag_deliver',
'post_mail', 'post_like' , 'republish', 'chat' ];
$ret['limits'] = PermissionLimits::Std_Limits();
break;
case 'custom':
default:
break;
}
$x = get_config('system','role_perms');
// let system settings over-ride any or all
// let system settings over-ride any or all
if($x && is_array($x) && array_key_exists($role,$x))
$ret = array_merge($ret,$x[$role]);
/**
* @hooks get_role_perms
* * \e array
*/
call_hooks('get_role_perms', $ret);
call_hooks('get_role_perms',$ret);
return $ret;
}
static public function new_custom_perms($uid,$perm,$abooks) {
// set permissionlimits for this permission here, for example:
// if($perm === 'mynewperm')
// \Zotlabs\Access\PermissionLimits::Set($uid,$perm,1);
if($perm === 'view_wiki')
\Zotlabs\Access\PermissionLimits::Set($uid, $perm, PERMS_PUBLIC);
if($perm === 'write_wiki')
\Zotlabs\Access\PermissionLimits::Set($uid, $perm, PERMS_SPECIFIC);
// set autoperms here if applicable
// choices are to set to 0, 1, or the value of an existing perm
if(get_pconfig($uid,'system','autoperms')) {
$c = channelx_by_n($uid);
$value = 0;
// if($perm === 'mynewperm')
// $value = get_abconfig($uid,$c['channel_hash'],'autoperms','someexistingperm');
if($perm === 'view_wiki')
$value = get_abconfig($uid,$c['channel_hash'],'autoperms','view_pages');
if($perm === 'write_wiki')
$value = get_abconfig($uid,$c['channel_hash'],'autoperms','write_pages');
if($c) {
set_abconfig($uid,$c['channel_hash'],'autoperms',$perm,$value);
}
}
// now set something for all existing connections.
if($abooks) {
foreach($abooks as $ab) {
switch($perm) {
// case 'mynewperm':
// choices are to set to 1, set to 0, or clone an existing perm
// set_abconfig($uid,$ab['abook_xchan'],'my_perms',$perm,
// intval(get_abconfig($uid,$ab['abook_xchan'],'my_perms','someexistingperm')));
case 'view_wiki':
set_abconfig($uid,$ab['abook_xchan'],'my_perms',$perm,
intval(get_abconfig($uid,$ab['abook_xchan'],'my_perms','view_pages')));
case 'write_wiki':
set_abconfig($uid,$ab['abook_xchan'],'my_perms',$perm,
intval(get_abconfig($uid,$ab['abook_xchan'],'my_perms','write_pages')));
default:
break;
}
}
}
}
/**
* @brief Array with translated role names and grouping.
*
* Return an associative array with grouped role names that can be used
* to create select groups like in \e field_select_grouped.tpl.
*
* @return array
*/
static public function roles() {
$roles = [
$roles = [
t('Social Networking') => [
'social_federation' => t('Social - Federation'),
'social' => t('Social - Mostly Public'),
'social_restricted' => t('Social - Restricted'),
'social' => t('Social - Mostly Public'),
'social_restricted' => t('Social - Restricted'),
'social_private' => t('Social - Private')
],
t('Community Forum') => [
'forum' => t('Forum - Mostly Public'),
'forum_restricted' => t('Forum - Restricted'),
'forum' => t('Forum - Mostly Public'),
'forum_restricted' => t('Forum - Restricted'),
'forum_private' => t('Forum - Private')
],
t('Feed Republish') => [
'feed' => t('Feed - Mostly Public'),
'feed' => t('Feed - Mostly Public'),
'feed_restricted' => t('Feed - Restricted')
],
t('Special Purpose') => [
'soapbox' => t('Special - Celebrity/Soapbox'),
'soapbox' => t('Special - Celebrity/Soapbox'),
'repository' => t('Special - Group Repository')
],
t('Other') => [
'custom' => t('Custom/Expert Mode')
]
];
call_hooks('list_permission_roles',$roles);
return $roles;
return $roles;
}
}

View File

@@ -1,55 +1,29 @@
<?php
namespace Zotlabs\Access;
use Zotlabs\Lib as Zlib;
/**
* @brief Extensible permissions.
*
* To add new permissions, add to the list of $perms below, with a simple description.
*
* Also visit PermissionRoles.php and add to the $ret['perms_connect'] property for any role
* if this permission should be granted to new connections.
*
* Next look at PermissionRoles::new_custom_perms() and provide a handler for updating custom
* permission roles. You will want to set a default PermissionLimit for each channel and also
* provide a sane default for any existing connections. You may or may not wish to provide a
* default auto permission. If in doubt, leave this alone as custom permissions by definition
* are the responsibility of the channel owner to manage. You just don't want to create any
* suprises or break things so you have an opportunity to provide sane settings.
*
* Update the version here and in PermissionRoles.
*
*
* Permissions with 'view' in the name are considered read permissions. Anything
* else requires authentication. Read permission limits are PERMS_PUBLIC and anything else
* is given PERMS_SPECIFIC.
*
* PermissionLimits::Std_limits() retrieves the standard limits. A permission role
* MAY alter an individual setting after retrieving the Std_limits if you require
* something different for a specific permission within the given role.
*
*/
class Permissions {
/**
* @brief Permissions version.
* Extensible permissions.
* To add new permissions, add to the list of $perms below, with a simple description.
* Also visit PermissionRoles.php and add to the $ret['perms_connect'] property for any role
* if this permission should be granted to new connections.
*
* This must match the version in PermissionRoles.php before permission updates can run.
* Permissions with 'view' in the name are considered read permissions. Anything
* else requires authentication. Read permission limits are PERMS_PUBLIC and anything else
* is given PERMS_SPECIFIC.
*
* PermissionLimits::Std_limits() retrieves the standard limits. A permission role
* MAY alter an individual setting after retrieving the Std_limits if you require
* something different for a specific permission within the given role.
*
* @return number
*/
static public function version() {
return 2;
}
/**
* @brief Return an array with Permissions.
*
* @param string $filter (optional) only passed to hook permissions_list
* @return array Associative array with permissions and short description.
*/
static public function Perms($filter = '') {
$perms = [
@@ -60,43 +34,29 @@ class Permissions {
'view_storage' => t('Can view my file storage and photos'),
'write_storage' => t('Can upload/modify my file storage and photos'),
'view_pages' => t('Can view my channel webpages'),
'view_wiki' => t('Can view my wiki pages'),
'write_pages' => t('Can create/edit my channel webpages'),
'write_wiki' => t('Can write to my wiki pages'),
'post_wall' => t('Can post on my channel (wall) page'),
'post_comments' => t('Can comment on or like my posts'),
'post_mail' => t('Can send me private mail messages'),
'post_like' => t('Can like/dislike profiles and profile things'),
'tag_deliver' => t('Can forward to all my channel connections via ! mentions in posts'),
'tag_deliver' => t('Can forward to all my channel connections via @+ mentions in posts'),
'chat' => t('Can chat with me'),
'republish' => t('Can source my public posts in derived channels'),
'delegate' => t('Can administer my channel')
];
$x = [
'permissions' => $perms,
'filter' => $filter
];
/**
* @hooks permissions_list
* * \e array \b permissions
* * \e string \b filter
*/
call_hooks('permissions_list', $x);
$x = array('permissions' => $perms, 'filter' => $filter);
call_hooks('permissions_list',$x);
return($x['permissions']);
}
/**
* @brief Perms from the above list that are blocked from anonymous observers.
*
* e.g. you must be authenticated.
*
* @return array Associative array with permissions and short description.
*/
static public function BlockedAnonPerms() {
$res = [];
// Perms from the above list that are blocked from anonymous observers.
// e.g. you must be authenticated.
$res = array();
$perms = PermissionLimits::Std_limits();
foreach($perms as $perm => $limit) {
if($limit != PERMS_PUBLIC) {
@@ -104,71 +64,30 @@ class Permissions {
}
}
$x = ['permissions' => $res];
/**
* @hooks write_perms
* * \e array \b permissions
*/
call_hooks('write_perms', $x);
$x = array('permissions' => $res);
call_hooks('write_perms',$x);
return($x['permissions']);
}
/**
* @brief Converts indexed perms array to associative perms array.
*
* Converts [ 0 => 'view_stream', ... ]
* to [ 'view_stream' => 1 ] for any permissions in $arr;
* Undeclared permissions which exist in Perms() are added and set to 0.
*
* @param array $arr
* @return array
*/
static public function FilledPerms($arr) {
if(is_null($arr)) {
btlogger('FilledPerms: null');
$arr = [];
}
// converts [ 0 => 'view_stream', ... ]
// to [ 'view_stream' => 1 ]
// for any permissions in $arr;
// Undeclared permissions are set to 0
static public function FilledPerms($arr) {
$everything = self::Perms();
$ret = [];
foreach($everything as $k => $v) {
if(in_array($k, $arr))
if(in_array($k,$arr))
$ret[$k] = 1;
else
$ret[$k] = 0;
}
return $ret;
}
/**
* @brief Convert perms array to indexed array.
*
* Converts [ 'view_stream' => 1 ] for any permissions in $arr
* to [ 0 => ['name' => 'view_stream', 'value' => 1], ... ]
*
* @param array $arr associative perms array 'view_stream' => 1
* @return array Indexed array with elements that look like
* * \e string \b name the perm name (e.g. view_stream)
* * \e int \b value the value of the perm (e.g. 1)
*/
static public function OPerms($arr) {
$ret = [];
if($arr) {
foreach($arr as $k => $v) {
$ret[] = [ 'name' => $k, 'value' => $v ];
}
}
return $ret;
}
/**
* @brief
*
* @param int $channel_id
* @return boolean|array
*/
static public function FilledAutoperms($channel_id) {
if(! intval(get_pconfig($channel_id,'system','autoperms')))
return false;
@@ -179,108 +98,19 @@ class Permissions {
);
if($r) {
foreach($r as $rr) {
$arr[$rr['k']] = intval($rr['v']);
$arr[$rr['k']] = $arr[$rr['v']];
}
}
return $arr;
}
/**
* @brief Compares that all Permissions from $p1 exist also in $p2.
*
* @param array $p1 The perms that have to exist in $p2
* @param array $p2 The perms to compare against
* @return boolean true if all perms from $p1 exist also in $p2
*/
static public function PermsCompare($p1, $p2) {
static public function PermsCompare($p1,$p2) {
foreach($p1 as $k => $v) {
if(! array_key_exists($k, $p2))
if(! array_key_exists($k,$p2))
return false;
if($p1[$k] != $p2[$k])
return false;
}
return true;
}
/**
* @brief
*
* @param int $channel_id A channel id
* @return array Associative array with
* * \e array \b perms Permission array
* * \e int \b automatic 0 or 1
*/
static public function connect_perms($channel_id) {
$my_perms = [];
$permcat = null;
$automatic = 0;
// If a default permcat exists, use that
$pc = ((feature_enabled($channel_id,'permcats')) ? get_pconfig($channel_id,'system','default_permcat') : 'default');
if(! in_array($pc, [ '','default' ])) {
$pcp = new Zlib\Permcat($channel_id);
$permcat = $pcp->fetch($pc);
if($permcat && $permcat['perms']) {
foreach($permcat['perms'] as $p) {
$my_perms[$p['name']] = $p['value'];
}
}
}
// look up the permission role to see if it specified auto-connect
// and if there was no permcat or a default permcat, set the perms
// from the role
$role = get_pconfig($channel_id,'system','permissions_role');
if($role) {
$xx = PermissionRoles::role_perms($role);
if($xx['perms_auto'])
$automatic = 1;
if((! $my_perms) && ($xx['perms_connect'])) {
$default_perms = $xx['perms_connect'];
$my_perms = Permissions::FilledPerms($default_perms);
}
}
// If we reached this point without having any permission information,
// it is likely a custom permissions role. First see if there are any
// automatic permissions.
if(! $my_perms) {
$m = Permissions::FilledAutoperms($channel_id);
if($m) {
$automatic = 1;
$my_perms = $m;
}
}
// If we reached this point with no permissions, the channel is using
// custom perms but they are not automatic. They will be stored in abconfig with
// the channel's channel_hash (the 'self' connection).
if(! $my_perms) {
$r = q("select channel_hash from channel where channel_id = %d",
intval($channel_id)
);
if($r) {
$x = q("select * from abconfig where chan = %d and xchan = '%s' and cat = 'my_perms'",
intval($channel_id),
dbesc($r[0]['channel_hash'])
);
if($x) {
foreach($x as $xv) {
$my_perms[$xv['k']] = intval($xv['v']);
}
}
}
}
return ( [ 'perms' => $my_perms, 'automatic' => $automatic ] );
}
}

View File

@@ -1,14 +0,0 @@
<?php
namespace Zotlabs\Daemon;
require_once('include/zot.php');
class Addon {
static public function run($argc,$argv) {
call_hooks('daemon_addon',$argv);
}
}

View File

@@ -3,6 +3,7 @@
namespace Zotlabs\Daemon;
require_once('include/zot.php');
require_once('include/hubloc.php');
class Checksites {

View File

@@ -50,19 +50,14 @@ class Cron {
// expire any expired items
$r = q("select id,item_wall from item where expires > '2001-01-01 00:00:00' and expires < %s
$r = q("select id from item where expires > '2001-01-01 00:00:00' and expires < %s
and item_deleted = 0 ",
db_utcnow()
);
if($r) {
require_once('include/items.php');
foreach($r as $rr) {
drop_item($rr['id'],false,(($rr['item_wall']) ? DROPITEM_PHASE1 : DROPITEM_NORMAL));
if($rr['item_wall']) {
// The notifier isn't normally invoked unless item_drop is interactive.
Zotlabs\Daemon\Master::Summon( [ 'Notifier', 'drop', $rr['id'] ] );
}
}
foreach($r as $rr)
drop_item($rr['id'],false);
}
@@ -83,7 +78,7 @@ class Cron {
// channels and sites that quietly vanished and prevent the directory from accumulating stale
// or dead entries.
$r = q("select channel_id from channel where channel_dirdate < %s - INTERVAL %s and channel_removed = 0",
$r = q("select channel_id from channel where channel_dirdate < %s - INTERVAL %s",
db_utcnow(),
db_quoteinterval('30 DAY')
);
@@ -126,9 +121,6 @@ class Cron {
}
}
require_once('include/attach.php');
attach_upgrade();
$abandon_days = intval(get_config('system','account_abandon_days'));
if($abandon_days < 1)
$abandon_days = 0;
@@ -179,8 +171,7 @@ class Cron {
// pull in some public posts
$disable_discover_tab = get_config('system','disable_discover_tab') || get_config('system','disable_discover_tab') === false;
if(! $disable_discover_tab)
if(! get_config('system','disable_discover_tab'))
Master::Summon(array('Externals'));
$generation = 0;

View File

@@ -38,20 +38,12 @@ class Cron_daily {
db_utcnow(), db_quoteinterval('30 DAY')
);
// expire any unread notifications over a year old
q("delete from notify where seen = 0 and created < %s - INTERVAL %s",
db_utcnow(), db_quoteinterval('1 YEAR')
);
//update statistics in config
require_once('include/statistics_fns.php');
update_channels_total_stat();
update_channels_active_halfyear_stat();
update_channels_active_monthly_stat();
update_local_posts_stat();
update_local_comments_stat();
// expire old delivery reports
@@ -84,11 +76,12 @@ class Cron_daily {
Master::Summon(array('Expire'));
Master::Summon(array('Cli_suggest'));
require_once('include/hubloc.php');
remove_obsolete_hublocs();
call_hooks('cron_daily',datetime_convert());
set_config('system','last_expire_day',intval(datetime_convert('UTC','UTC','now','d')));
set_config('system','last_expire_day',$d2);
/**
* End Cron Daily

View File

@@ -17,25 +17,11 @@ class Cron_weekly {
z_check_cert();
require_once('include/hubloc.php');
prune_hub_reinstalls();
mark_orphan_hubsxchans();
// Find channels that were removed in the last three weeks, but
// haven't been finally cleaned up. These should be older than 10
// days to ensure that "purgeall" messages have gone out or bounced
// or timed out.
$r = q("select channel_id from channel where channel_removed = 1 and
channel_deleted > %s - INTERVAL %s and channel_deleted < %s - INTERVAL %s",
db_utcnow(), db_quoteinterval('21 DAY'),
db_utcnow(), db_quoteinterval('10 DAY')
);
if($r) {
foreach($r as $rv) {
channel_remove_final($rv['channel_id']);
}
}
// get rid of really old poco records

View File

@@ -53,9 +53,6 @@ class Deliver {
remove_queue_item($r[0]['outq_hash']);
if($dresult && is_array($dresult)) {
// delivery reports for local deliveries do not require encryption
foreach($dresult as $xx) {
if(is_array($xx) && array_key_exists('message_id',$xx)) {
if(delivery_report_is_storable($xx)) {
@@ -75,8 +72,6 @@ class Deliver {
q("delete from dreport where dreport_queue = '%s'",
dbesc($argv[$x])
);
continue;
}
}

View File

@@ -34,8 +34,7 @@ class Expire {
logger('expire: start', LOGGER_DEBUG);
$site_expire = intval(get_config('system', 'default_expire_days'));
$commented_days = intval(get_config('system','active_expire_days'));
$site_expire = get_config('system', 'default_expire_days');
logger('site_expire: ' . $site_expire);
@@ -65,7 +64,7 @@ class Expire {
// if the site or service class expiration is non-zero and less than person expiration, use that
logger('Expire: ' . $rr['channel_address'] . ' interval: ' . $expire_days, LOGGER_DEBUG);
item_expire($rr['channel_id'], $expire_days, $commented_days);
item_expire($rr['channel_id'], $expire_days);
}
}
@@ -86,7 +85,7 @@ class Expire {
logger('Expire: sys interval: ' . $expire_days, LOGGER_DEBUG);
if ($expire_days)
item_expire($x['channel_id'], $expire_days, $commented_days);
item_expire($x['channel_id'], $expire_days);
logger('Expire: sys: done', LOGGER_DEBUG);
}

View File

@@ -17,7 +17,7 @@ class Gprobe {
if(! strpos($url,'@'))
return;
$r = q("select * from hubloc where hubloc_addr = '%s' limit 1",
$r = q("select * from xchan where xchan_addr = '%s' limit 1",
dbesc($url)
);

View File

@@ -21,18 +21,12 @@ class Importdoc {
$files = glob("$d/$f");
if($files) {
foreach($files as $fi) {
if($fi === 'doc/html') {
if($fi === 'doc/html')
continue;
}
if(is_dir($fi)) {
if(is_dir($fi))
self::update_docs_dir("$fi/*");
}
else {
// don't update media content
if(strpos(z_mime_content_type($fi),'text') === 0) {
store_doc_file($fi);
}
}
else
store_doc_file($fi);
}
}
}

View File

@@ -1,47 +0,0 @@
<?php /** @file */
namespace Zotlabs\Daemon;
class Importfile {
static public function run($argc,$argv){
logger('Importfile: ' . print_r($argv,true));
if($argc < 3)
return;
$channel = channelx_by_n($argv[1]);
if(! $channel)
return;
$srcfile = $argv[2];
$folder = (($argc > 3) ? $argv[3] : '');
$dstname = (($argc > 4) ? $argv[4] : '');
$hash = random_string();
$arr = [
'src' => $srcfile,
'filename' => (($dstname) ? $dstname : basename($srcfile)),
'hash' => $hash,
'allow_cid' => $channel['channel_allow_cid'],
'allow_gid' => $channel['channel_allow_gid'],
'deny_cid' => $channel['channel_deny_cid'],
'deny_gid' => $channel['channel_deny_gid'],
'preserve_original' => true,
'replace' => true
];
if($folder)
$arr['folder'] = $folder;
attach_store($channel,$channel['channel_hash'],'import',$arr);
$sync = attach_export_data($channel,$hash);
if($sync)
build_sync_packet($channel['channel_id'],array('file' => array($sync)));
return;
}
}

View File

@@ -24,7 +24,8 @@ class Master {
static public function Release($argc,$argv) {
cli_startup();
logger('Master: release: ' . print_r($argv,true), LOGGER_ALL,LOG_DEBUG);
require_once('Zotlabs/Daemon/' . $argv[0] . '.php');
$cls = '\\Zotlabs\\Daemon\\' . $argv[0];
$cls::run($argc,$argv);
}
}
}

View File

@@ -4,12 +4,6 @@ namespace Zotlabs\Daemon;
require_once('include/queue_fn.php');
require_once('include/html2plain.php');
require_once('include/conversation.php');
require_once('include/zot.php');
require_once('include/items.php');
require_once('include/bbcode.php');
/*
* This file was at one time responsible for doing all deliveries, but this caused
@@ -59,8 +53,6 @@ require_once('include/bbcode.php');
*
* ZOT
* permission_create abook_id
* permission_accept abook_id
* permission_reject abook_id
* permission_update abook_id
* refresh_all channel_id
* purge_all channel_id
@@ -71,11 +63,17 @@ require_once('include/bbcode.php');
* location channel_id
* request channel_id xchan_hash message_id
* rating xlink_id
* keychange channel_id
*
*/
require_once('include/zot.php');
require_once('include/queue_fn.php');
require_once('include/datetime.php');
require_once('include/items.php');
require_once('include/bbcode.php');
require_once('include/channel.php');
class Notifier {
@@ -90,6 +88,8 @@ class Notifier {
$item_id = $argv[2];
$extra = (($argc > 3) ? $argv[3] : null);
if(! $item_id)
return;
@@ -97,6 +97,16 @@ class Notifier {
$deliveries = array();
$dead_hubs = array();
$dh = q("select site_url from site where site_dead = 1");
if($dh) {
foreach($dh as $dead) {
$dead_hubs[] = $dead['site_url'];
}
}
$request = false;
$mail = false;
$top_level = false;
@@ -110,7 +120,7 @@ class Notifier {
$normal_mode = false;
$mail = true;
$private = true;
$message = q("SELECT * FROM mail WHERE id = %d LIMIT 1",
$message = q("SELECT * FROM `mail` WHERE `id` = %d LIMIT 1",
intval($item_id)
);
if(! $message) {
@@ -147,21 +157,7 @@ class Notifier {
$packet_type = 'request';
$normal_mode = false;
}
elseif($cmd === 'keychange') {
$channel = channelx_by_n($item_id);
$r = q("select abook_xchan from abook where abook_channel = %d",
intval($item_id)
);
if($r) {
foreach($r as $rr) {
$recipients[] = $rr['abook_xchan'];
}
}
$private = false;
$packet_type = 'keychange';
$normal_mode = false;
}
elseif(in_array($cmd, [ 'permission_update', 'permission_reject', 'permission_accept', 'permission_create' ])) {
elseif($cmd == 'permission_update' || $cmd == 'permission_create') {
// Get the (single) recipient
$r = q("select * from abook left join xchan on abook_xchan = xchan_hash where abook_id = %d and abook_self = 0",
intval($item_id)
@@ -173,12 +169,8 @@ class Notifier {
if($channel) {
$perm_update = array('sender' => $channel, 'recipient' => $r[0], 'success' => false, 'deliveries' => '');
if($cmd === 'permission_create')
if($cmd == 'permission_create')
call_hooks('permissions_create',$perm_update);
elseif($cmd === 'permission_accept')
call_hooks('permissions_accept',$perm_update);
elseif($cmd === 'permission_reject')
call_hooks('permissions_reject',$perm_update);
else
call_hooks('permissions_update',$perm_update);
@@ -282,15 +274,14 @@ class Notifier {
$deleted_item = true;
}
if(! in_array(intval($target_item['item_type']), [ ITEM_TYPE_POST ] )) {
if(intval($target_item['item_type']) != ITEM_TYPE_POST) {
logger('notifier: target item not forwardable: type ' . $target_item['item_type'], LOGGER_DEBUG);
return;
}
// Check for non published items, but allow an exclusion for transmitting hidden file activities
if(intval($target_item['item_unpublished']) || intval($target_item['item_delayed']) ||
intval($target_item['item_blocked']) ||
if(intval($target_item['item_unpublished']) || intval($target_item['item_delayed']) ||
( intval($target_item['item_hidden']) && ($target_item['obj_type'] !== ACTIVITY_OBJ_FILE))) {
logger('notifier: target item not published, so not forwardable', LOGGER_DEBUG);
return;
@@ -313,7 +304,7 @@ class Notifier {
}
if($target_item['mid'] === $target_item['parent_mid']) {
if($target_item['id'] == $target_item['parent']) {
$parent_item = $target_item;
$top_level_post = true;
}
@@ -380,13 +371,12 @@ class Notifier {
if(! $encoded_item['flags'])
$encoded_item['flags'] = array();
$encoded_item['flags'][] = 'relay';
$upstream = true;
}
else {
logger('notifier: normal distribution', LOGGER_DEBUG);
if($cmd === 'relay')
logger('notifier: owner relay');
$upstream = false;
// if our parent is a tag_delivery recipient, uplink to the original author causing
// a delivery fork.
@@ -411,6 +401,7 @@ class Notifier {
return;
}
}
}
$walltowall = (($top_level_post && $channel['xchan_hash'] === $target_item['author_xchan']) ? true : false);
@@ -424,16 +415,14 @@ class Notifier {
logger('notifier: encoded item: ' . print_r($x,true), LOGGER_DATA, LOG_DEBUG);
stringify_array_elms($recipients);
if(! $recipients) {
logger('no recipients');
if(! $recipients)
return;
}
// logger('notifier: recipients: ' . print_r($recipients,true), LOGGER_NORMAL, LOG_DEBUG);
// logger('notifier: recipients: ' . print_r($recipients,true), LOGGER_NORMAL, LOG_DEBUG);
$env_recips = (($private) ? array() : null);
$details = q("select xchan_hash, xchan_instance_url, xchan_network, xchan_addr, xchan_guid, xchan_guid_sig from xchan where xchan_hash in (" . protect_sprintf(implode(',',$recipients)) . ")");
$details = q("select xchan_hash, xchan_instance_url, xchan_network, xchan_addr, xchan_guid, xchan_guid_sig from xchan where xchan_hash in (" . implode(',',$recipients) . ")");
$recip_list = array();
@@ -442,40 +431,39 @@ class Notifier {
foreach($details as $d) {
$recip_list[] = $d['xchan_addr'] . ' (' . $d['xchan_hash'] . ')';
if($private) {
$env_recips[] = [
'guid' => $d['xchan_guid'],
'guid_sig' => $d['xchan_guid_sig'],
'hash' => $d['xchan_hash']
];
if($private)
$env_recips[] = array('guid' => $d['xchan_guid'],'guid_sig' => $d['xchan_guid_sig'],'hash' => $d['xchan_hash']);
if($d['xchan_network'] === 'mail' && $normal_mode) {
$delivery_options = get_xconfig($d['xchan_hash'],'system','delivery_mode');
if(! $delivery_options)
format_and_send_email($channel,$d,$target_item);
}
}
}
$narr = [
'channel' => $channel,
'upstream' => $upstream,
'env_recips' => $env_recips,
'packet_recips' => $packet_recips,
'recipients' => $recipients,
'item' => $item,
'target_item' => $target_item,
'parent_item' => $parent_item,
$narr = array(
'channel' => $channel,
'env_recips' => $env_recips,
'packet_recips' => $packet_recips,
'recipients' => $recipients,
'item' => $item,
'target_item' => $target_item,
'top_level_post' => $top_level_post,
'private' => $private,
'private' => $private,
'relay_to_owner' => $relay_to_owner,
'uplink' => $uplink,
'cmd' => $cmd,
'mail' => $mail,
'single' => (($cmd === 'single_mail' || $cmd === 'single_activity') ? true : false),
'location' => $location,
'request' => $request,
'normal_mode' => $normal_mode,
'packet_type' => $packet_type,
'walltowall' => $walltowall,
'queued' => []
];
'uplink' => $uplink,
'cmd' => $cmd,
'mail' => $mail,
'single' => (($cmd === 'single_mail' || $cmd === 'single_activity') ? true : false),
'location' => $location,
'request' => $request,
'normal_mode' => $normal_mode,
'packet_type' => $packet_type,
'walltowall' => $walltowall,
'queued' => array()
);
call_hooks('notifier_process', $narr);
if($narr['queued']) {
@@ -498,10 +486,10 @@ class Notifier {
// Now we have collected recipients (except for external mentions, FIXME)
// Let's reduce this to a set of hubs; checking that the site is not dead.
// Let's reduce this to a set of hubs.
$r = q("select hubloc.*, site.site_crypto, site.site_flags from hubloc left join site on site_url = hubloc_url where hubloc_hash in (" . protect_sprintf(implode(',',$recipients)) . ")
and hubloc_error = 0 and hubloc_deleted = 0 and ( site_dead = 0 OR site_dead is null ) "
$r = q("select * from hubloc where hubloc_hash in (" . implode(',',$recipients) . ")
and hubloc_error = 0 and hubloc_deleted = 0"
);
@@ -512,78 +500,72 @@ class Notifier {
$hubs = $r;
/**
* Reduce the hubs to those that are unique. For zot hubs, we need to verify uniqueness by the sitekey,
* since it may have been a re-install which has not yet been detected and pruned.
* Reduce the hubs to those that are unique. For zot hubs, we need to verify uniqueness by the sitekey, since it may have been
* a re-install which has not yet been detected and pruned.
* For other networks which don't have or require sitekeys, we'll have to use the URL
*/
$hublist = []; // this provides an easily printable list for the logs
$dhubs = []; // delivery hubs where we store our resulting unique array
$keys = []; // array of keys to check uniquness for zot hubs
$urls = []; // array of urls to check uniqueness of hubs from other networks
$hub_env = []; // per-hub envelope so we don't broadcast the entire envelope to all
$hublist = array(); // this provides an easily printable list for the logs
$dhubs = array(); // delivery hubs where we store our resulting unique array
$keys = array(); // array of keys to check uniquness for zot hubs
$urls = array(); // array of urls to check uniqueness of hubs from other networks
foreach($hubs as $hub) {
if($env_recips) {
foreach($env_recips as $er) {
if($hub['hubloc_hash'] === $er['hash']) {
if(! array_key_exists($hub['hubloc_host'] . $hub['hubloc_sitekey'], $hub_env)) {
$hub_env[$hub['hubloc_host'] . $hub['hubloc_sitekey']] = [];
}
$hub_env[$hub['hubloc_host'] . $hub['hubloc_sitekey']][] = $er;
}
}
if(in_array($hub['hubloc_url'],$dead_hubs)) {
logger('skipping dead hub: ' . $hub['hubloc_url'], LOGGER_DEBUG, LOG_INFO);
continue;
}
if($hub['hubloc_network'] == 'zot') {
if(! in_array($hub['hubloc_sitekey'],$keys)) {
$hublist[] = $hub['hubloc_host'] . ' ' . $hub['hubloc_network'];
$dhubs[] = $hub;
$keys[] = $hub['hubloc_sitekey'];
$hublist[] = $hub['hubloc_host'];
$dhubs[] = $hub;
$keys[] = $hub['hubloc_sitekey'];
}
}
else {
if(! in_array($hub['hubloc_url'],$urls)) {
$hublist[] = $hub['hubloc_host'] . ' ' . $hub['hubloc_network'];
$dhubs[] = $hub;
$urls[] = $hub['hubloc_url'];
$hublist[] = $hub['hubloc_host'];
$dhubs[] = $hub;
$urls[] = $hub['hubloc_url'];
}
}
}
logger('notifier: will notify/deliver to these hubs: ' . print_r($hublist,true), LOGGER_DEBUG, LOG_DEBUG);
foreach($dhubs as $hub) {
if($hub['hubloc_network'] !== 'zot') {
$narr = [
'channel' => $channel,
'upstream' => $upstream,
'env_recips' => $env_recips,
'packet_recips' => $packet_recips,
'recipients' => $recipients,
'item' => $item,
'target_item' => $target_item,
'parent_item' => $parent_item,
'hub' => $hub,
$narr = array(
'channel' => $channel,
'env_recips' => $env_recips,
'packet_recips' => $packet_recips,
'recipients' => $recipients,
'item' => $item,
'target_item' => $target_item,
'hub' => $hub,
'top_level_post' => $top_level_post,
'private' => $private,
'private' => $private,
'relay_to_owner' => $relay_to_owner,
'uplink' => $uplink,
'cmd' => $cmd,
'mail' => $mail,
'single' => (($cmd === 'single_mail' || $cmd === 'single_activity') ? true : false),
'location' => $location,
'request' => $request,
'normal_mode' => $normal_mode,
'packet_type' => $packet_type,
'walltowall' => $walltowall,
'queued' => []
];
'uplink' => $uplink,
'cmd' => $cmd,
'mail' => $mail,
'single' => (($cmd === 'single_mail' || $cmd === 'single_activity') ? true : false),
'location' => $location,
'request' => $request,
'normal_mode' => $normal_mode,
'packet_type' => $packet_type,
'walltowall' => $walltowall,
'queued' => array()
);
call_hooks('notifier_hub',$narr);
@@ -596,13 +578,13 @@ class Notifier {
}
// singleton deliveries by definition 'not got zot'.
// Single deliveries are other federated networks (plugins) and we're essentially
// Single deliveries are other federated networks (plugins) and we're essentially
// delivering only to those that have this site url in their abook_instance
// and only from within a sync operation. This means if you post from a clone,
// and a connection is connected to one of your other clones; assuming that hub
// is running it will receive a sync packet. On receipt of this sync packet it
// will invoke a delivery to those connections which are connected to just that
// hub instance.
// hub instance.
if($cmd === 'single_mail' || $cmd === 'single_activity') {
continue;
@@ -610,21 +592,15 @@ class Notifier {
// default: zot protocol
$hash = random_string();
$hash = random_string();
$packet = null;
$pmsg = '';
if($packet_type === 'refresh' || $packet_type === 'purge') {
$packet = zot_build_packet($channel,$packet_type,(($packet_recips) ? $packet_recips : null));
}
if($packet_type === 'keychange') {
$pmsg = get_pconfig($channel['channel_id'],'system','keychange');
$packet = zot_build_packet($channel,$packet_type,(($packet_recips) ? $packet_recips : null));
}
elseif($packet_type === 'request') {
$env = (($hub_env && $hub_env[$hub['hubloc_host'] . $hub['hubloc_sitekey']]) ? $hub_env[$hub['hubloc_host'] . $hub['hubloc_sitekey']] : '');
$packet = zot_build_packet($channel,$packet_type,$env,$hub['hubloc_sitekey'],$hub['site_crypto'],
$hash, array('message_id' => $request_message_id)
$packet = zot_build_packet($channel,$packet_type,$env_recips,$hub['hubloc_sitekey'],$hash,
array('message_id' => $request_message_id)
);
}
@@ -634,37 +610,19 @@ class Notifier {
'account_id' => $channel['channel_account_id'],
'channel_id' => $channel['channel_id'],
'posturl' => $hub['hubloc_callback'],
'notify' => $packet,
'msg' => (($pmsg) ? json_encode($pmsg) : '')
'notify' => $packet
));
}
else {
$env = (($hub_env && $hub_env[$hub['hubloc_host'] . $hub['hubloc_sitekey']]) ? $hub_env[$hub['hubloc_host'] . $hub['hubloc_sitekey']] : '');
// currently zot6 delivery is only performed on normal items and not sync items or mail or anything else
// Eventually we will do this for all deliveries, but for now ensure this is precisely what we are dealing
// with before switching to zot6 as the primary zot6 handler checks for the existence of a message delivery report
// to trigger dequeue'ing
$z6 = (($encoded_item && $encoded_item['type'] === 'activity' && (! array_key_exists('allow_cid',$encoded_item))) ? true : false);
if($z6) {
$packet = zot6_build_packet($channel,'notify',$env, json_encode($encoded_item), (($private) ? $hub['hubloc_sitekey'] : null), $hub['site_crypto'],$hash);
}
else {
$packet = zot_build_packet($channel,'notify',$env, (($private) ? $hub['hubloc_sitekey'] : null), $hub['site_crypto'],$hash);
}
queue_insert(
[
'hash' => $hash,
'account_id' => $target_item['aid'],
'channel_id' => $target_item['uid'],
'posturl' => $hub['hubloc_callback'],
'notify' => $packet,
'msg' => json_encode($encoded_item)
]
);
$packet = zot_build_packet($channel,'notify',$env_recips,(($private) ? $hub['hubloc_sitekey'] : null),$hash);
queue_insert(array(
'hash' => $hash,
'account_id' => $target_item['aid'],
'channel_id' => $target_item['uid'],
'posturl' => $hub['hubloc_callback'],
'notify' => $packet,
'msg' => json_encode($encoded_item)
));
// only create delivery reports for normal undeleted items
if(is_array($target_item) && array_key_exists('postopts',$target_item) && (! $target_item['item_deleted']) && (! get_config('system','disable_dreport'))) {
@@ -685,9 +643,9 @@ class Notifier {
if($normal_mode) {
$x = q("select * from hook where hook = 'notifier_normal'");
if($x) {
Master::Summon( [ 'Deliver_hooks', $target_item['id'] ] );
}
if($x)
Master::Summon(array('Deliver_hooks',$target_item['id']));
}
if($deliveries)

View File

@@ -118,29 +118,13 @@ class Onepoll {
if($fetch_feed) {
if(strpos($contact['xchan_connurl'],z_root()) === 0) {
// local channel - save a network fetch
$c = channelx_by_hash($contact['xchan_hash']);
if($c) {
$x = [
'success' => true,
'body' => json_encode( [
'success' => true,
'messages' => zot_feed($c['channel_id'], $importer['xchan_hash'], [ 'mindate' => $last_update ])
])
];
}
}
else {
// remote fetch
$feedurl = str_replace('/poco/','/zotfeed/',$contact['xchan_connurl']);
$feedurl .= '?f=&mindate=' . urlencode($last_update);
$feedurl = str_replace('/poco/','/zotfeed/',$contact['xchan_connurl']);
$feedurl .= '?f=&mindate=' . urlencode($last_update) . '&zid=' . $importer['channel_address'] . '@' . \App::get_hostname();
$recurse = 0;
$x = z_fetch_url($feedurl, false, $recurse, [ 'session' => true ]);
}
$x = z_fetch_url($feedurl);
logger('feed_update: ' . print_r($x,true), LOGGER_DATA);
}
if(($x) && ($x['success'])) {

View File

@@ -71,18 +71,14 @@ class Poller {
$randfunc = db_getfunc('RAND');
$contacts = q("SELECT abook.abook_updated, abook.abook_connected, abook.abook_feed,
abook.abook_channel, abook.abook_id, abook.abook_archived, abook.abook_pending,
abook.abook_ignored, abook.abook_blocked,
xchan.xchan_network,
account.account_lastlog, account.account_flags
FROM abook LEFT JOIN xchan on abook_xchan = xchan_hash
$contacts = q("SELECT * FROM abook LEFT JOIN xchan on abook_xchan = xchan_hash
LEFT JOIN account on abook_account = account_id
where abook_self = 0
$sql_extra
AND (( account_flags = %d ) OR ( account_flags = %d )) $abandon_sql ORDER BY $randfunc",
intval(ACCOUNT_OK),
intval(ACCOUNT_UNVERIFIED) // FIXME
);
if($contacts) {

View File

@@ -12,11 +12,10 @@ class Queue {
require_once('include/items.php');
require_once('include/bbcode.php');
if($argc > 1)
$queue_id = $argv[1];
if(argc() > 1)
$queue_id = argv(1);
else
$queue_id = EMPTY_STR;
$queue_id = 0;
logger('queue: start');
@@ -62,15 +61,30 @@ class Queue {
// the site is permanently down, there's no reason to attempt delivery at all, or at most not more than once
// or twice a day.
$r = q("SELECT * FROM outq WHERE outq_delivered = 0 and outq_scheduled < %s ",
db_utcnow()
// FIXME: can we sort postgres on outq_priority and maintain the 'distinct' ?
// The order by max(outq_priority) might be a dodgy query because of the group by.
// The desired result is to return a sequence in the order most likely to be delivered in this run.
// If a hub has already been sitting in the queue for a few days, they should be delivered last;
// hence every failure should drop them further down the priority list.
if(ACTIVE_DBTYPE == DBTYPE_POSTGRES) {
$prefix = 'DISTINCT ON (outq_posturl)';
$suffix = 'ORDER BY outq_posturl';
} else {
$prefix = '';
$suffix = 'GROUP BY outq_posturl ORDER BY max(outq_priority)';
}
$r = q("SELECT $prefix * FROM outq WHERE outq_delivered = 0 and (( outq_created > %s - INTERVAL %s and outq_updated < %s - INTERVAL %s ) OR ( outq_updated < %s - INTERVAL %s )) $suffix",
db_utcnow(), db_quoteinterval('12 HOUR'),
db_utcnow(), db_quoteinterval('15 MINUTE'),
db_utcnow(), db_quoteinterval('1 HOUR')
);
}
if(! $r)
return;
foreach($r as $rv) {
queue_deliver($rv);
foreach($r as $rr) {
queue_deliver($rr);
}
}
}

View File

@@ -77,7 +77,7 @@ class Ratenotif {
continue;
$hash = random_string();
$n = zot_build_packet($channel,'notify',null,null,'',$hash);
$n = zot_build_packet($channel,'notify',null,null,$hash);
queue_insert(array(
'hash' => $hash,
@@ -88,14 +88,6 @@ class Ratenotif {
'msg' => json_encode($encoded_item)
));
$x = q("select count(outq_hash) as total from outq where outq_delivered = 0");
if(intval($x[0]['total']) > intval(get_config('system','force_queue_threshold',300))) {
logger('immediate delivery deferred.', LOGGER_DEBUG, LOG_INFO);
update_queue_item($hash);
continue;
}
$deliver[] = $hash;
if(count($deliver) >= $deliveries_per_process) {

View File

@@ -1,78 +0,0 @@
<?php /** @file */
namespace Zotlabs\Daemon;
class Thumbnail {
static public function run($argc,$argv) {
if(! $argc == 2)
return;
$c = q("select * from attach where hash = '%s' ",
dbesc($argv[1])
);
if(! $c)
return;
$attach = $c[0];
$preview_style = intval(get_config('system','thumbnail_security',0));
$preview_width = intval(get_config('system','thumbnail_width',300));
$preview_height = intval(get_config('system','thumbnail_height',300));
$p = [
'attach' => $attach,
'preview_style' => $preview_style,
'preview_width' => $preview_width,
'preview_height' => $preview_height,
'thumbnail' => null
];
/**
* @hooks thumbnail
* * \e array \b attach
* * \e int \b preview_style
* * \e int \b preview_width
* * \e int \b preview_height
* * \e string \b thumbnail
*/
call_hooks('thumbnail',$p);
if($p['thumbnail']) {
return;
}
$default_controller = null;
$files = glob('Zotlabs/Thumbs/*.php');
if($files) {
foreach($files as $f) {
$clsname = '\\Zotlabs\\Thumbs\\' . ucfirst(basename($f,'.php'));
if(class_exists($clsname)) {
$x = new $clsname();
if(method_exists($x,'Match')) {
$matched = $x->Match($attach['filetype']);
if($matched) {
$x->Thumb($attach,$preview_style,$preview_width,$preview_height);
}
}
if(method_exists($x,'MatchDefault')) {
$default_matched = $x->MatchDefault(substr($attach['filetype'],0,strpos($attach['filetype'],'/')));
if($default_matched) {
$default_controller = $x;
}
}
}
}
}
if(($default_controller)
&& ((! file_exists(dbunescbin($attach['content']) . '.thumb'))
|| (filectime(dbunescbin($attach['content']) . 'thumb') < (time() - 60)))) {
$default_controller->Thumb($attach,$preview_style,$preview_width,$preview_height);
}
}
}

View File

@@ -2,12 +2,7 @@
namespace Zotlabs\Extend;
use App;
/**
* @brief Hook class.
*
*/
class Hook {
static public function register($hook,$file,$function,$version = 1,$priority = 0) {
@@ -15,7 +10,7 @@ class Hook {
$function = serialize($function);
}
$r = q("SELECT * FROM hook WHERE hook = '%s' AND file = '%s' AND fn = '%s' and priority = %d and hook_version = %d LIMIT 1",
$r = q("SELECT * FROM `hook` WHERE `hook` = '%s' AND `file` = '%s' AND `fn` = '%s' and priority = %d and hook_version = %d LIMIT 1",
dbesc($hook),
dbesc($file),
dbesc($function),
@@ -28,13 +23,13 @@ class Hook {
// To aid in upgrade and transition, remove old settings for any registered hooks that match in all respects except
// for priority or hook_version
$r = q("DELETE FROM hook where hook = '%s' and file = '%s' and fn = '%s'",
$r = q("DELETE FROM `hook` where `hook` = '%s' and `file` = '%s' and `fn` = '%s'",
dbesc($hook),
dbesc($file),
dbesc($function)
);
$r = q("INSERT INTO hook (hook, file, fn, priority, hook_version) VALUES ( '%s', '%s', '%s', %d, %d )",
$r = q("INSERT INTO `hook` (`hook`, `file`, `fn`, `priority`, `hook_version`) VALUES ( '%s', '%s', '%s', %d, %d )",
dbesc($hook),
dbesc($file),
dbesc($function),
@@ -45,20 +40,11 @@ class Hook {
return $r;
}
static public function register_array($file,$arr) {
if($arr) {
foreach($arr as $k => $v) {
self::register($k,$file,$v);
}
}
}
static public function unregister($hook,$file,$function,$version = 1,$priority = 0) {
if(is_array($function)) {
$function = serialize($function);
}
$r = q("DELETE FROM hook WHERE hook = '%s' AND file = '%s' AND fn = '%s' and priority = %d and hook_version = %d",
$r = q("DELETE FROM hook WHERE hook = '%s' AND `file` = '%s' AND `fn` = '%s' and priority = %d and hook_version = %d",
dbesc($hook),
dbesc($file),
dbesc($function),
@@ -69,21 +55,19 @@ class Hook {
return $r;
}
/**
* @brief Unregister all hooks with this file component.
*
* Useful for addon upgrades where you want to clean out old interfaces.
*
* @param string $file
*/
// unregister all hooks with this file component.
// Useful for addon upgrades where you want to clean out old interfaces.
static public function unregister_by_file($file) {
$r = q("DELETE FROM hook WHERE file = '%s' ",
$r = q("DELETE FROM hook WHERE `file` = '%s' ",
dbesc($file)
);
return $r;
}
/**
* @brief Inserts a hook into a page request.
*
@@ -105,6 +89,7 @@ class Hook {
* @param int $priority
* currently not implemented in this function, would require the hook array to be resorted
*/
static public function insert($hook, $fn, $version = 0, $priority = 0) {
if(is_array($fn)) {
$fn = serialize($fn);

View File

@@ -1,34 +0,0 @@
<?php
namespace Zotlabs\Identity;
class OAuth2Server extends \OAuth2\Server {
public function __construct(OAuth2Storage $storage, $config = []) {
if(! is_array($config)) {
$config = [
'use_openid_connect' => true,
'issuer' => \Zotlabs\Lib\System::get_site_name()
];
}
parent::__construct($storage, $config);
// Add the "Client Credentials" grant type (it is the simplest of the grant types)
$this->addGrantType(new \OAuth2\GrantType\ClientCredentials($storage));
// Add the "Authorization Code" grant type (this is where the oauth magic happens)
$this->addGrantType(new \OAuth2\GrantType\AuthorizationCode($storage));
$keyStorage = new \OAuth2\Storage\Memory( [
'keys' => [
'public_key' => get_config('system', 'pubkey'),
'private_key' => get_config('system', 'prvkey')
]
]);
$this->addStorage($keyStorage, 'public_key');
}
}

View File

@@ -1,81 +0,0 @@
<?php
namespace Zotlabs\Identity;
class OAuth2Storage extends \OAuth2\Storage\Pdo {
/**
* @param string $username
* @param string $password
* @return bool
*/
public function checkUserCredentials($username, $password)
{
if ($user = $this->getUser($username)) {
return $this->checkPassword($user, $password);
}
return false;
}
/**
* @param string $username
* @return array|bool
*/
public function getUserDetails($username)
{
return $this->getUser($username);
}
/**
*
* @param array $user
* @param string $password
* @return bool
*/
protected function checkPassword($user, $password)
{
$x = account_verify_password($user,$password);
return((array_key_exists('channel',$x) && ! empty($x['channel'])) ? true : false);
}
/**
* @param string $username
* @return array|bool
*/
public function getUser($username)
{
$x = channelx_by_nick($username);
if(! $x) {
return false;
}
return( [
'username' => $x['channel_address'],
'user_id' => $x['channel_id'],
'firstName' => $x['channel_name'],
'lastName' => '',
'password' => 'NotARealPassword'
] );
}
/**
* plaintext passwords are bad! Override this for your application
*
* @param string $username
* @param string $password
* @param string $firstName
* @param string $lastName
* @return bool
*/
public function setUser($username, $password, $firstName = null, $lastName = null)
{
return true;
}
}

View File

@@ -10,8 +10,8 @@ class AConfig {
return XConfig::Load('a_' . $account_id);
}
static public function Get($account_id,$family,$key,$default = false) {
return XConfig::Get('a_' . $account_id,$family,$key, $default);
static public function Get($account_id,$family,$key) {
return XConfig::Get('a_' . $account_id,$family,$key);
}
static public function Set($account_id,$family,$key,$value) {

View File

@@ -16,7 +16,7 @@ class AbConfig {
}
static public function Get($chan,$xhash,$family,$key, $default = false) {
static public function Get($chan,$xhash,$family,$key) {
$r = q("select * from abconfig where chan = %d and xchan = '%s' and cat = '%s' and k = '%s' limit 1",
intval($chan),
dbesc($xhash),
@@ -26,7 +26,7 @@ class AbConfig {
if($r) {
return ((preg_match('|^a:[0-9]+:{.*}$|s', $r[0]['v'])) ? unserialize($r[0]['v']) : $r[0]['v']);
}
return $default;
return false;
}

View File

@@ -1,276 +0,0 @@
<?php
namespace Zotlabs\Lib;
/**
* @brief ActivityStreams class.
*
* Parses an ActivityStream JSON string.
*/
class ActivityStreams {
public $raw = null;
public $data;
public $valid = false;
public $id = '';
public $type = '';
public $actor = null;
public $obj = null;
public $tgt = null;
public $origin = null;
public $owner = null;
public $signer = null;
public $ldsig = null;
public $sigok = false;
public $recips = null;
public $raw_recips = null;
/**
* @brief Constructor for ActivityStreams.
*
* Takes a JSON string as parameter, decodes it and sets up this object.
*
* @param string $string
*/
function __construct($string) {
$this->raw = $string;
$this->data = json_decode($string, true);
if($this->data) {
$this->valid = true;
}
if($this->is_valid()) {
$this->id = $this->get_property_obj('id');
$this->type = $this->get_primary_type();
$this->actor = $this->get_compound_property('actor');
$this->obj = $this->get_compound_property('object');
$this->tgt = $this->get_compound_property('target');
$this->origin = $this->get_compound_property('origin');
$this->recips = $this->collect_recips();
$this->ldsig = $this->get_compound_property('signature');
if($this->ldsig) {
$this->signer = $this->get_compound_property('creator',$this->ldsig);
if($this->signer && $this->signer['publicKey'] && $this->signer['publicKey']['publicKeyPem']) {
$this->sigok = \Zotlabs\Lib\LDSignatures::verify($this->data,$this->signer['publicKey']['publicKeyPem']);
}
}
if(($this->type === 'Note') && (! $this->obj)) {
$this->obj = $this->data;
$this->type = 'Create';
}
}
}
/**
* @brief Return if instantiated ActivityStream is valid.
*
* @return boolean Return true if the JSON string could be decoded.
*/
function is_valid() {
return $this->valid;
}
function set_recips($arr) {
$this->saved_recips = $arr;
}
/**
* @brief Collects all recipients.
*
* @param string $base
* @param string $namespace (optional) default empty
* @return array
*/
function collect_recips($base = '', $namespace = '') {
$x = [];
$fields = [ 'to', 'cc', 'bto', 'bcc', 'audience'];
foreach($fields as $f) {
$y = $this->get_compound_property($f, $base, $namespace);
if($y) {
$x = array_merge($x, $y);
if(! is_array($this->raw_recips))
$this->raw_recips = [];
$this->raw_recips[$f] = $x;
}
}
// not yet ready for prime time
// $x = $this->expand($x,$base,$namespace);
return $x;
}
function expand($arr,$base = '',$namespace = '') {
$ret = [];
// right now use a hardwired recursion depth of 5
for($z = 0; $z < 5; $z ++) {
if(is_array($arr) && $arr) {
foreach($arr as $a) {
if(is_array($a)) {
$ret[] = $a;
}
else {
$x = $this->get_compound_property($a,$base,$namespace);
if($x) {
$ret = array_merge($ret,$x);
}
}
}
}
}
/// @fixme de-duplicate
return $ret;
}
/**
* @brief
*
* @param array $base
* @param string $namespace if not set return empty string
* @return string|NULL
*/
function get_namespace($base, $namespace) {
if(! $namespace)
return '';
$key = null;
foreach( [ $this->data, $base ] as $b ) {
if(! $b)
continue;
if(array_key_exists('@context', $b)) {
if(is_array($b['@context'])) {
foreach($b['@context'] as $ns) {
if(is_array($ns)) {
foreach($ns as $k => $v) {
if($namespace === $v)
$key = $k;
}
}
else {
if($namespace === $ns) {
$key = '';
}
}
}
}
else {
if($namespace === $b['@context']) {
$key = '';
}
}
}
}
return $key;
}
/**
* @brief
*
* @param string $property
* @param array $base (optional)
* @param string $namespace (optional) default empty
* @return NULL|mixed
*/
function get_property_obj($property, $base = '', $namespace = '') {
$prefix = $this->get_namespace($base, $namespace);
if($prefix === null)
return null;
$base = (($base) ? $base : $this->data);
$propname = (($prefix) ? $prefix . ':' : '') . $property;
return ((array_key_exists($propname, $base)) ? $base[$propname] : null);
}
/**
* @brief Fetches a property from an URL.
*
* @param string $url
* @return NULL|mixed
*/
function fetch_property($url) {
$redirects = 0;
if(! check_siteallowed($url)) {
logger('blacklisted: ' . $url);
return null;
}
$x = z_fetch_url($url, true, $redirects,
['headers' => [ 'Accept: application/activity+json, application/ld+json; profile="https://www.w3.org/ns/activitystreams"' ]]);
if($x['success'])
return json_decode($x['body'], true);
return null;
}
/**
* @brief
*
* @param string $property
* @param array $base
* @param string $namespace (optional) default empty
* @return NULL|mixed
*/
function get_compound_property($property, $base = '', $namespace = '') {
$x = $this->get_property_obj($property, $base, $namespace);
if($this->is_url($x)) {
$x = $this->fetch_property($x);
}
return $x;
}
/**
* @brief Check if string starts with http.
*
* @param string $url
* @return boolean
*/
function is_url($url) {
if(($url) && (! is_array($url)) && (strpos($url, 'http') === 0)) {
return true;
}
return false;
}
/**
* @brief Gets the type property.
*
* @param array $base
* @param string $namespace (optional) default empty
* @return NULL|mixed
*/
function get_primary_type($base = '', $namespace = '') {
if(! $base)
$base = $this->data;
$x = $this->get_property_obj('type', $base, $namespace);
if(is_array($x)) {
foreach($x as $y) {
if(strpos($y, ':') === false) {
return $y;
}
}
}
return $x;
}
function debug() {
$x = var_export($this, true);
return $x;
}
}

View File

@@ -13,12 +13,7 @@ require_once('include/channel.php');
class Apps {
static public $available_apps = null;
static public $installed_apps = null;
static public $base_apps = null;
static public $installed_system_apps = null;
static public function get_system_apps($translate = true) {
@@ -39,19 +34,16 @@ class Apps {
if($files) {
foreach($files as $f) {
$path = explode('/',$f);
$plugin = trim($path[1]);
$plugin = $path[1];
if(plugin_is_installed($plugin)) {
$x = self::parse_app_description($f,$translate);
if($x) {
$x['plugin'] = $plugin;
$ret[] = $x;
}
}
}
}
call_hooks('get_system_apps',$ret);
return $ret;
}
@@ -60,52 +52,23 @@ class Apps {
static public function import_system_apps() {
if(! local_channel())
return;
self::$base_apps = get_config('system','base_apps',[
'Connections',
'Suggest Channels',
'Grid',
'Settings',
'Files',
'Channel Home',
'View Profile',
'Photos',
'Events',
'Directory',
'Search',
'Help',
'Mail',
'Profile Photo'
]);
$apps = self::get_system_apps(false);
self::$available_apps = q("select * from app where app_channel = 0");
self::$installed_apps = q("select * from app where app_channel = %d",
self::$installed_system_apps = q("select * from app where app_system = 1 and app_channel = %d",
intval(local_channel())
);
if($apps) {
foreach($apps as $app) {
$id = self::check_install_system_app($app);
// $id will be boolean true or false to install an app, or an integer id to update an existing app
if($id !== false) {
$app['uid'] = 0;
$app['guid'] = hash('whirlpool',$app['name']);
$app['system'] = 1;
self::app_install(0,$app);
}
$id = self::check_install_personal_app($app);
// $id will be boolean true or false to install an app, or an integer id to update an existing app
if($id === false)
continue;
if($id !== true) {
// if we already installed this app, but it changed, preserve any categories we created
$s = EMPTY_STR;
$r = q("select term from term where otype = %d and oid = %d",
$s = '';
$r = q("select * from term where otype = %d and oid = d",
intval(TERM_OBJ_APP),
intval($id)
);
@@ -122,7 +85,6 @@ class Apps {
$app['guid'] = hash('whirlpool',$app['name']);
$app['system'] = 1;
self::app_install(local_channel(),$app);
}
}
}
@@ -133,56 +95,28 @@ class Apps {
*/
static public function check_install_system_app($app) {
if((! is_array(self::$available_apps)) || (! count(self::$available_apps))) {
if((! is_array(self::$installed_system_apps)) || (! count(self::$installed_system_apps))) {
return true;
}
$notfound = true;
foreach(self::$available_apps as $iapp) {
foreach(self::$installed_system_apps as $iapp) {
if($iapp['app_id'] == hash('whirlpool',$app['name'])) {
$notfound = false;
if(($iapp['app_version'] != $app['version'])
|| ($app['plugin'] && (! $iapp['app_plugin']))) {
if($iapp['app_version'] != $app['version']) {
return intval($iapp['app_id']);
}
}
}
return $notfound;
}
/**
* Install the system app if no system apps have been installed, or if a new system app
* is discovered, or if the version of a system app changes.
*/
static public function check_install_personal_app($app) {
$installed = false;
foreach(self::$installed_apps as $iapp) {
if($iapp['app_id'] == hash('whirlpool',$app['name'])) {
$installed = true;
if(($iapp['app_version'] != $app['version'])
|| ($app['plugin'] && (! $iapp['app_plugin']))) {
return intval($iapp['app_id']);
}
}
}
if(! $installed && in_array($app['name'],self::$base_apps)) {
return true;
}
return false;
}
static public function app_name_compare($a,$b) {
return strcasecmp($a['name'],$b['name']);
}
static public function parse_app_description($f,$translate = true) {
$ret = array();
$baseurl = z_root();
@@ -210,11 +144,8 @@ class Apps {
$ret['type'] = 'system';
foreach($ret as $k => $v) {
if(strpos($v,'http') === 0) {
if(! (local_channel() && strpos($v,z_root()) === 0)) {
$ret[$k] = zid($v);
}
}
if(strpos($v,'http') === 0)
$ret[$k] = zid($v);
}
if(array_key_exists('desc',$ret))
@@ -226,21 +157,11 @@ class Apps {
if(array_key_exists('version',$ret))
$ret['version'] = str_replace(array('\'','"'),array('&#39;','&dquot;'),$ret['version']);
if(array_key_exists('categories',$ret))
$ret['categories'] = str_replace(array('\'','"'),array('&#39;','&dquot;'),$ret['categories']);
if(array_key_exists('requires',$ret)) {
$requires = explode(',',$ret['requires']);
foreach($requires as $require) {
$require = trim(strtolower($require));
$config = false;
if(substr($require, 0, 7) == 'config:') {
$config = true;
$require = ltrim($require, 'config:');
$require = explode('=', $require);
}
switch($require) {
case 'nologin':
if(local_channel())
@@ -258,22 +179,15 @@ class Apps {
if(! is_public_profile())
unset($ret);
break;
case 'public_stream':
if(! can_view_public_stream())
unset($ret);
break;
case 'observer':
if(! $observer)
unset($ret);
break;
default:
if($config)
$unset = ((get_config('system', $require[0]) == $require[1]) ? false : true);
else
$unset = ((local_channel() && feature_enabled(local_channel(),$require)) ? false : true);
if($unset)
if(! (local_channel() && feature_enabled(local_channel(),$require)))
unset($ret);
break;
}
}
}
@@ -288,19 +202,17 @@ class Apps {
static public function translate_system_apps(&$arr) {
$apps = array(
'Apps' => t('Apps'),
'Articles' => t('Articles'),
'Cards' => t('Cards'),
'Admin' => t('Site Admin'),
'Report Bug' => t('Report Bug'),
'Site Admin' => t('Site Admin'),
'Bug Report' => t('Bug Report'),
'View Bookmarks' => t('View Bookmarks'),
'My Chatrooms' => t('My Chatrooms'),
'Connections' => t('Connections'),
'Firefox Share' => t('Firefox Share'),
'Remote Diagnostics' => t('Remote Diagnostics'),
'Suggest Channels' => t('Suggest Channels'),
'Login' => t('Login'),
'Channel Manager' => t('Channel Manager'),
'Grid' => t('Activity'),
'Grid' => t('Grid'),
'Settings' => t('Settings'),
'Files' => t('Files'),
'Webpages' => t('Webpages'),
@@ -326,19 +238,9 @@ class Apps {
'Profile Photo' => t('Profile Photo')
);
if(array_key_exists('name',$arr)) {
if(array_key_exists($arr['name'],$apps)) {
$arr['name'] = $apps[$arr['name']];
}
}
else {
for($x = 0; $x < count($arr); $x++) {
if(array_key_exists($arr[$x]['name'],$apps)) {
$arr[$x]['name'] = $apps[$arr[$x]['name']];
}
}
}
if(array_key_exists($arr['name'],$apps))
$arr['name'] = $apps[$arr['name']];
}
@@ -350,11 +252,9 @@ class Apps {
* modes:
* view: normal mode for viewing an app via bbcode from a conversation or page
* provides install/update button if you're logged in locally
* install: like view but does not display app-bin options if they are present
* list: normal mode for viewing an app on the app page
* no buttons are shown
* edit: viewing the app page in editing mode provides a delete button
* nav: render apps for app-bin
*/
$installed = false;
@@ -363,40 +263,25 @@ class Apps {
return;
if(! $papp['photo'])
$papp['photo'] = 'icon:gear';
$papp['photo'] = z_root() . '/' . get_default_profile_photo(80);
self::translate_system_apps($papp);
if(trim($papp['plugin']) && (! plugin_is_installed(trim($papp['plugin']))))
return '';
$papp['papp'] = self::papp_encode($papp);
if(! strstr($papp['url'],'://'))
$papp['url'] = z_root() . ((strpos($papp['url'],'/') === 0) ? '' : '/') . $papp['url'];
foreach($papp as $k => $v) {
if(strpos($v,'http') === 0 && $k != 'papp') {
if(! (local_channel() && strpos($v,z_root()) === 0)) {
$papp[$k] = zid($v);
}
}
if(strpos($v,'http') === 0 && $k != 'papp')
$papp[$k] = zid($v);
if($k === 'desc')
$papp['desc'] = str_replace(array('\'','"'),array('&#39;','&dquot;'),$papp['desc']);
if($k === 'requires') {
$requires = explode(',',$v);
foreach($requires as $require) {
$require = trim(strtolower($require));
$config = false;
if(substr($require, 0, 7) == 'config:') {
$config = true;
$require = ltrim($require, 'config:');
$require = explode('=', $require);
}
switch($require) {
case 'nologin':
if(local_channel())
@@ -414,23 +299,16 @@ class Apps {
if(! is_public_profile())
return '';
break;
case 'public_stream':
if(! can_view_public_stream())
return '';
break;
case 'observer':
$observer = \App::get_observer();
if(! $observer)
return '';
break;
default:
if($config)
$unset = ((get_config('system', $require[0]) === $require[1]) ? false : true);
else
$unset = ((local_channel() && feature_enabled(local_channel(),$require)) ? false : true);
if($unset)
if(! (local_channel() && feature_enabled(local_channel(),$require)))
return '';
break;
}
}
}
@@ -454,38 +332,14 @@ class Apps {
}
$install_action = (($installed) ? t('Update') : t('Install'));
$icon = ((strpos($papp['photo'],'icon:') === 0) ? substr($papp['photo'],5) : '');
if($mode === 'navbar') {
return replace_macros(get_markup_template('app_nav.tpl'),array(
'$app' => $papp,
'$icon' => $icon,
));
}
if($mode === 'install') {
$papp['embed'] = true;
}
return replace_macros(get_markup_template('app.tpl'),array(
'$app' => $papp,
'$icon' => $icon,
'$hosturl' => $hosturl,
'$purchase' => (($papp['page'] && (! $installed)) ? t('Purchase') : ''),
'$install' => (($hosturl && in_array($mode, ['view','install'])) ? $install_action : ''),
'$install' => (($hosturl && $mode == 'view') ? $install_action : ''),
'$edit' => ((local_channel() && $installed && $mode == 'edit') ? t('Edit') : ''),
'$delete' => ((local_channel() && $installed && $mode == 'edit') ? t('Delete') : ''),
'$undelete' => ((local_channel() && $installed && $mode == 'edit') ? t('Undelete') : ''),
'$deleted' => $papp['deleted'],
'$feature' => (($papp['embed']) ? false : true),
'$pin' => (($papp['embed']) ? false : true),
'$featured' => ((strpos($papp['categories'], 'nav_featured_app') === false) ? false : true),
'$pinned' => ((strpos($papp['categories'], 'nav_pinned_app') === false) ? false : true),
'$navapps' => (($mode == 'nav') ? true : false),
'$order' => (($mode == 'nav-order') ? true : false),
'$add' => t('Add to app-tray'),
'$remove' => t('Remove from app-tray'),
'$add_nav' => t('Pin to navbar'),
'$remove_nav' => t('Unpin from navbar')
'$delete' => ((local_channel() && $installed && $mode == 'edit') ? t('Delete') : '')
));
}
@@ -505,7 +359,7 @@ class Apps {
if($r) {
if(! $r[0]['app_system']) {
if($app['categories'] && (! $app['term'])) {
$r[0]['term'] = q("select * from term where otype = %d and oid = %d",
$r[0]['term'] = q("select * from term where otype = %d and oid = d",
intval(TERM_OBJ_APP),
intval($r[0]['id'])
);
@@ -528,12 +382,18 @@ class Apps {
intval($uid)
);
if($x) {
if(! intval($x[0]['app_deleted'])) {
$x[0]['app_deleted'] = 1;
q("delete from term where otype = %d and oid = %d",
intval(TERM_OBJ_APP),
intval($x[0]['id'])
$x[0]['app_deleted'] = 1;
q("delete from term where otype = %d and oid = %d",
intval(TERM_OBJ_APP),
intval($x[0]['id'])
);
if($x[0]['app_system']) {
$r = q("update app set app_deleted = 1 where app_id = '%s' and app_channel = %d",
dbesc($app['guid']),
intval($uid)
);
}
else {
$r = q("delete from app where app_id = '%s' and app_channel = %d",
dbesc($app['guid']),
intval($uid)
@@ -542,62 +402,16 @@ class Apps {
// we don't sync system apps - they may be completely different on the other system
build_sync_packet($uid,array('app' => $x));
}
else {
self::app_undestroy($uid,$app);
}
}
}
}
static public function app_undestroy($uid,$app) {
// undelete a system app
if($uid && $app['guid']) {
$x = q("select * from app where app_id = '%s' and app_channel = %d limit 1",
dbesc($app['guid']),
intval($uid)
);
if($x) {
if($x[0]['app_system']) {
$r = q("update app set app_deleted = 0 where app_id = '%s' and app_channel = %d",
dbesc($app['guid']),
intval($uid)
);
}
}
}
}
static public function app_feature($uid,$app,$term) {
$r = q("select id from app where app_id = '%s' and app_channel = %d limit 1",
dbesc($app['guid']),
intval($uid)
);
$x = q("select * from term where otype = %d and oid = %d and term = '%s' limit 1",
intval(TERM_OBJ_APP),
intval($r[0]['id']),
dbesc($term)
);
if($x) {
q("delete from term where otype = %d and oid = %d and term = '%s'",
intval(TERM_OBJ_APP),
intval($x[0]['oid']),
dbesc($term)
);
}
else {
store_item_tag($uid, $r[0]['id'], TERM_OBJ_APP, TERM_CATEGORY, $term, escape_tags(z_root() . '/apps/?f=&cat=' . $term));
}
}
static public function app_installed($uid,$app) {
$r = q("select id from app where app_id = '%s' and app_channel = %d limit 1",
$r = q("select id from app where app_id = '%s' and app_version = '%s' and app_channel = %d limit 1",
dbesc((array_key_exists('guid',$app)) ? $app['guid'] : ''),
dbesc((array_key_exists('version',$app)) ? $app['version'] : ''),
intval($uid)
);
return(($r) ? true : false);
@@ -605,27 +419,16 @@ class Apps {
}
static public function app_list($uid, $deleted = false, $cats = []) {
static public function app_list($uid, $deleted = false, $cat = '') {
if($deleted)
$sql_extra = "";
$sql_extra = " and app_deleted = 1 ";
else
$sql_extra = " and app_deleted = 0 ";
if($cats) {
$cat_sql_extra = " and ( ";
foreach($cats as $cat) {
if(strpos($cat_sql_extra, 'term'))
$cat_sql_extra .= "or ";
$cat_sql_extra .= "term = '" . dbesc($cat) . "' ";
}
$cat_sql_extra .= ") ";
$r = q("select oid from term where otype = %d $cat_sql_extra",
intval(TERM_OBJ_APP)
if($cat) {
$r = q("select oid from term where otype = %d and term = '%s'",
intval(TERM_OBJ_APP),
dbesc($cat)
);
if(! $r)
return $r;
@@ -642,7 +445,6 @@ class Apps {
$r = q("select * from app where app_channel = %d $sql_extra order by app_name asc",
intval($uid)
);
if($r) {
for($x = 0; $x < count($r); $x ++) {
if(! $r[$x]['app_system'])
@@ -653,133 +455,9 @@ class Apps {
);
}
}
return($r);
}
static public function app_order($uid,$apps) {
if(! $apps)
return $apps;
$x = (($uid) ? get_pconfig($uid,'system','app_order') : get_config('system','app_order'));
if(($x) && (! is_array($x))) {
$y = explode(',',$x);
$y = array_map('trim',$y);
$x = $y;
}
if(! (is_array($x) && ($x)))
return $apps;
$ret = [];
foreach($x as $xx) {
$y = self::find_app_in_array($xx,$apps);
if($y) {
$ret[] = $y;
}
}
foreach($apps as $ap) {
if(! self::find_app_in_array($ap['name'],$ret)) {
$ret[] = $ap;
}
}
return $ret;
}
static function find_app_in_array($name,$arr) {
if(! $arr)
return false;
foreach($arr as $x) {
if($x['name'] === $name) {
return $x;
}
}
return false;
}
static function moveup($uid,$guid) {
$syslist = array();
$list = self::app_list($uid, false, ['nav_featured_app', 'nav_pinned_app']);
if($list) {
foreach($list as $li) {
$syslist[] = self::app_encode($li);
}
}
self::translate_system_apps($syslist);
usort($syslist,'self::app_name_compare');
$syslist = self::app_order($uid,$syslist);
if(! $syslist)
return;
$newlist = [];
foreach($syslist as $k => $li) {
if($li['guid'] === $guid) {
$position = $k;
break;
}
}
if(! $position)
return;
$dest_position = $position - 1;
$saved = $syslist[$dest_position];
$syslist[$dest_position] = $syslist[$position];
$syslist[$position] = $saved;
$narr = [];
foreach($syslist as $x) {
$narr[] = $x['name'];
}
set_pconfig($uid,'system','app_order',implode(',',$narr));
}
static function movedown($uid,$guid) {
$syslist = array();
$list = self::app_list($uid, false, ['nav_featured_app', 'nav_pinned_app']);
if($list) {
foreach($list as $li) {
$syslist[] = self::app_encode($li);
}
}
self::translate_system_apps($syslist);
usort($syslist,'self::app_name_compare');
$syslist = self::app_order($uid,$syslist);
if(! $syslist)
return;
$newlist = [];
foreach($syslist as $k => $li) {
if($li['guid'] === $guid) {
$position = $k;
break;
}
}
if($position >= count($syslist) - 1)
return;
$dest_position = $position + 1;
$saved = $syslist[$dest_position];
$syslist[$dest_position] = $syslist[$position];
$syslist[$position] = $saved;
$narr = [];
foreach($syslist as $x) {
$narr[] = $x['name'];
}
set_pconfig($uid,'system','app_order',implode(',',$narr));
}
static public function app_decode($s) {
$x = base64_decode(str_replace(array('<br />',"\r","\n",' '),array('','','',''),$s));
@@ -789,25 +467,18 @@ class Apps {
static public function app_store($arr) {
//logger('app_store: ' . print_r($arr,true));
// logger('app_store: ' . print_r($arr,true));
$darray = array();
$ret = array('success' => false);
$sys = get_sys_channel();
$darray['app_url'] = ((x($arr,'url')) ? $arr['url'] : '');
$darray['app_channel'] = ((x($arr,'uid')) ? $arr['uid'] : 0);
if(! $darray['app_url'])
if((! $darray['app_url']) || (! $darray['app_channel']))
return $ret;
if((! $arr['uid']) && (! $arr['author'])) {
$arr['author'] = $sys['channel_hash'];
}
if($arr['photo'] && (strpos($arr['photo'],'icon:') !== 0) && (! strstr($arr['photo'],z_root()))) {
if($arr['photo'] && ! strstr($arr['photo'],z_root())) {
$x = import_xchan_photo($arr['photo'],get_observer_hash(),true);
$arr['photo'] = $x[1];
}
@@ -823,14 +494,13 @@ class Apps {
$darray['app_addr'] = ((x($arr,'addr')) ? escape_tags($arr['addr']) : '');
$darray['app_price'] = ((x($arr,'price')) ? escape_tags($arr['price']) : '');
$darray['app_page'] = ((x($arr,'page')) ? escape_tags($arr['page']) : '');
$darray['app_plugin'] = ((x($arr,'plugin')) ? escape_tags(trim($arr['plugin'])) : '');
$darray['app_requires'] = ((x($arr,'requires')) ? escape_tags($arr['requires']) : '');
$darray['app_system'] = ((x($arr,'system')) ? intval($arr['system']) : 0);
$darray['app_deleted'] = ((x($arr,'deleted')) ? intval($arr['deleted']) : 0);
$created = datetime_convert();
$r = q("insert into app ( app_id, app_sig, app_author, app_name, app_desc, app_url, app_photo, app_version, app_channel, app_addr, app_price, app_page, app_requires, app_created, app_edited, app_system, app_plugin, app_deleted ) values ( '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d, '%s', '%s', '%s', '%s', '%s', '%s', %d, '%s', %d )",
$r = q("insert into app ( app_id, app_sig, app_author, app_name, app_desc, app_url, app_photo, app_version, app_channel, app_addr, app_price, app_page, app_requires, app_created, app_edited, app_system, app_deleted ) values ( '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d, '%s', '%s', '%s', '%s', '%s', '%s', %d, %d )",
dbesc($darray['app_id']),
dbesc($darray['app_sig']),
dbesc($darray['app_author']),
@@ -847,10 +517,8 @@ class Apps {
dbesc($created),
dbesc($created),
intval($darray['app_system']),
dbesc($darray['app_plugin']),
intval($darray['app_deleted'])
);
if($r) {
$ret['success'] = true;
$ret['app_id'] = $darray['app_id'];
@@ -877,7 +545,6 @@ class Apps {
static public function app_update($arr) {
//logger('app_update: ' . print_r($arr,true));
$darray = array();
$ret = array('success' => false);
@@ -888,7 +555,7 @@ class Apps {
if((! $darray['app_url']) || (! $darray['app_channel']) || (! $darray['app_id']))
return $ret;
if($arr['photo'] && (strpos($arr['photo'],'icon:') !== 0) && (! strstr($arr['photo'],z_root()))) {
if($arr['photo'] && ! strstr($arr['photo'],z_root())) {
$x = import_xchan_photo($arr['photo'],get_observer_hash(),true);
$arr['photo'] = $x[1];
}
@@ -902,14 +569,13 @@ class Apps {
$darray['app_addr'] = ((x($arr,'addr')) ? escape_tags($arr['addr']) : '');
$darray['app_price'] = ((x($arr,'price')) ? escape_tags($arr['price']) : '');
$darray['app_page'] = ((x($arr,'page')) ? escape_tags($arr['page']) : '');
$darray['app_plugin'] = ((x($arr,'plugin')) ? escape_tags(trim($arr['plugin'])) : '');
$darray['app_requires'] = ((x($arr,'requires')) ? escape_tags($arr['requires']) : '');
$darray['app_system'] = ((x($arr,'system')) ? intval($arr['system']) : 0);
$darray['app_deleted'] = ((x($arr,'deleted')) ? intval($arr['deleted']) : 0);
$edited = datetime_convert();
$r = q("update app set app_sig = '%s', app_author = '%s', app_name = '%s', app_desc = '%s', app_url = '%s', app_photo = '%s', app_version = '%s', app_addr = '%s', app_price = '%s', app_page = '%s', app_requires = '%s', app_edited = '%s', app_system = %d, app_plugin = '%s', app_deleted = %d where app_id = '%s' and app_channel = %d",
$r = q("update app set app_sig = '%s', app_author = '%s', app_name = '%s', app_desc = '%s', app_url = '%s', app_photo = '%s', app_version = '%s', app_addr = '%s', app_price = '%s', app_page = '%s', app_requires = '%s', app_edited = '%s', app_system = %d, app_deleted = %d where app_id = '%s' and app_channel = %d",
dbesc($darray['app_sig']),
dbesc($darray['app_author']),
dbesc($darray['app_name']),
@@ -923,7 +589,6 @@ class Apps {
dbesc($darray['app_requires']),
dbesc($edited),
intval($darray['app_system']),
dbesc($darray['app_plugin']),
intval($darray['app_deleted']),
dbesc($darray['app_id']),
intval($darray['app_channel'])
@@ -937,12 +602,6 @@ class Apps {
dbesc($darray['app_id']),
intval($darray['app_channel'])
);
// if updating an embed app, don't mess with any existing categories.
if(array_key_exists('embed',$arr) && intval($arr['embed']) && (intval($darray['app_channel'])))
return $ret;
if($x) {
q("delete from term where otype = %d and oid = %d",
intval(TERM_OBJ_APP),
@@ -996,9 +655,6 @@ class Apps {
if($app['app_photo'])
$ret['photo'] = $app['app_photo'];
if($app['app_icon'])
$ret['icon'] = $app['app_icon'];
if($app['app_version'])
$ret['version'] = $app['app_version'];
@@ -1017,9 +673,6 @@ class Apps {
if($app['app_system'])
$ret['system'] = $app['app_system'];
if($app['app_plugin'])
$ret['plugin'] = trim($app['app_plugin']);
if($app['app_deleted'])
$ret['deleted'] = $app['app_deleted'];
@@ -1037,8 +690,6 @@ class Apps {
if(! $embed)
return $ret;
$ret['embed'] = true;
if(array_key_exists('categories',$ret))
unset($ret['categories']);

View File

@@ -9,10 +9,10 @@ namespace Zotlabs\Lib;
class Cache {
public static function get($key) {
$hash = hash('whirlpool',$key);
$key = substr($key,0,254);
$r = q("SELECT v FROM cache WHERE k = '%s' limit 1",
dbesc($hash)
dbesc($key)
);
if ($r)
@@ -22,20 +22,20 @@ class Cache {
public static function set($key,$value) {
$hash = hash('whirlpool',$key);
$key = substr($key,0,254);
$r = q("SELECT * FROM cache WHERE k = '%s' limit 1",
dbesc($hash)
dbesc($key)
);
if($r) {
q("UPDATE cache SET v = '%s', updated = '%s' WHERE k = '%s'",
dbesc($value),
dbesc(datetime_convert()),
dbesc($hash));
dbesc($key));
}
else {
q("INSERT INTO cache ( k, v, updated) VALUES ('%s','%s','%s')",
dbesc($hash),
dbesc($key),
dbesc($value),
dbesc(datetime_convert()));
}

View File

@@ -2,18 +2,22 @@
namespace Zotlabs\Lib;
/**
* @brief A class with chatroom related static methods.
* @brief Chat related functions.
*/
class Chatroom {
/**
* @brief Creates a chatroom.
*
* @param array $channel
* @param array $arr
* @return array An associative array containing:
* * \e boolean \b success - A boolean success status
* * \e string \b message - (optional) A string
* @return An associative array containing:
* - success: A boolean
* - message: (optional) A string
*/
static public function create($channel, $arr) {
$ret = array('success' => false);
@@ -146,8 +150,8 @@ class Chatroom {
}
if(intval($x[0]['cr_expire'])) {
$r = q("delete from chat where created < %s - INTERVAL %s and chat_room = %d",
db_utcnow(),
$r = q("delete from chat where created < %s - INTERVAL %s and chat_room = %d",
db_utcnow(),
db_quoteinterval( intval($x[0]['cr_expire']) . ' MINUTE' ),
intval($x[0]['cr_id'])
);
@@ -221,16 +225,10 @@ class Chatroom {
}
/**
* @brief Create a chat message via API.
*
* create a chat message via API.
* It is the caller's responsibility to enter the room.
*
* @param int $uid
* @param int $room_id
* @param string $xchan
* @param string $text
* @return array
*/
*/
static public function message($uid, $room_id, $xchan, $text) {
$ret = array('success' => false);
@@ -247,18 +245,12 @@ class Chatroom {
if(! $r)
return $ret;
$arr = [
$arr = array(
'chat_room' => $room_id,
'chat_xchan' => $xchan,
'chat_text' => $text
];
/**
* @hooks chat_message
* Called to create a chat message.
* * \e int \b chat_room
* * \e string \b chat_xchan
* * \e string \b chat_text
*/
);
call_hooks('chat_message', $arr);
$x = q("insert into chat ( chat_room, chat_xchan, created, chat_text )
@@ -266,7 +258,7 @@ class Chatroom {
intval($room_id),
dbesc($xchan),
dbesc(datetime_convert()),
dbesc(str_rot47(base64url_encode($arr['chat_text'])))
dbesc($arr['chat_text'])
);
$ret['success'] = true;

View File

@@ -1,4 +1,4 @@
<?php
<?php /** @file */
namespace Zotlabs\Lib;
@@ -14,6 +14,7 @@ class Config {
* @param string $family
* The category of the configuration value
*/
static public function Load($family) {
if(! array_key_exists($family, \App::$config))
\App::$config[$family] = array();
@@ -29,7 +30,7 @@ class Config {
}
\App::$config[$family]['config_loaded'] = true;
}
}
}
}
/**
@@ -46,12 +47,13 @@ class Config {
* @return mixed
* Return the set value, or false if the database update failed
*/
static public function Set($family, $key, $value) {
static public function Set($family,$key,$value) {
// manage array value
$dbvalue = ((is_array($value)) ? serialize($value) : $value);
$dbvalue = ((is_bool($dbvalue)) ? intval($dbvalue) : $dbvalue);
if(self::Get($family, $key) === false || (! self::get_from_storage($family, $key))) {
if(get_config($family, $key) === false || (! self::get_from_storage($family, $key))) {
$ret = q("INSERT INTO config ( cat, k, v ) VALUES ( '%s', '%s', '%s' ) ",
dbesc($family),
dbesc($key),
@@ -74,8 +76,8 @@ class Config {
\App::$config[$family][$key] = $value;
$ret = $value;
}
return $ret;
}
/**
@@ -86,31 +88,31 @@ class Config {
* $key from a cached storage in App::$config[$family]. If a key is found in the
* DB but does not exist in local config cache, pull it into the cache so we
* do not have to hit the DB again for this item.
*
*
* Returns false if not set.
*
* @param string $family
* The category of the configuration value
* @param string $key
* The configuration key to query
* @param string $default (optional) default false
* @return mixed Return value or false on error or if not set
*/
static public function Get($family, $key, $default = false) {
static public function Get($family,$key) {
if((! array_key_exists($family, \App::$config)) || (! array_key_exists('config_loaded', \App::$config[$family])))
self::Load($family);
if(array_key_exists('config_loaded', \App::$config[$family])) {
if(! array_key_exists($key, \App::$config[$family])) {
return $default;
return false;
}
return ((! is_array(\App::$config[$family][$key])) && (preg_match('|^a:[0-9]+:{.*}$|s', \App::$config[$family][$key]))
return ((! is_array(\App::$config[$family][$key])) && (preg_match('|^a:[0-9]+:{.*}$|s', \App::$config[$family][$key]))
? unserialize(\App::$config[$family][$key])
: \App::$config[$family][$key]
);
}
return $default;
return false;
}
/**
@@ -125,26 +127,25 @@ class Config {
* The configuration key to delete
* @return mixed
*/
static public function Delete($family, $key) {
static public function Delete($family,$key) {
$ret = false;
if(array_key_exists($family, \App::$config) && array_key_exists($key, \App::$config[$family]))
unset(\App::$config[$family][$key]);
$ret = q("DELETE FROM config WHERE cat = '%s' AND k = '%s'",
$ret = q("DELETE FROM config WHERE cat = '%s' AND k = '%s'",
dbesc($family),
dbesc($key)
);
return $ret;
}
/**
* @brief Returns a record directly from the database configuration storage.
* @brief Returns a value directly from the database configuration storage.
*
* This function queries directly the database and bypasses the cached storage
* This function queries directly the database and bypasses the chached storage
* from get_config($family, $key).
*
* @param string $family
@@ -153,12 +154,12 @@ class Config {
* The configuration key to query
* @return mixed
*/
static private function get_from_storage($family,$key) {
$ret = q("SELECT * FROM config WHERE cat = '%s' AND k = '%s' LIMIT 1",
dbesc($family),
dbesc($key)
);
return $ret;
}

View File

@@ -1,107 +0,0 @@
<?php
namespace Zotlabs\Lib;
class DB_Upgrade {
public $config_name = '';
public $func_prefix = '';
function __construct($db_revision) {
$this->config_name = 'db_version';
$this->func_prefix = '_';
$build = get_config('system', 'db_version', 0);
if(! intval($build))
$build = set_config('system', 'db_version', $db_revision);
if($build == $db_revision) {
// Nothing to be done.
return;
}
else {
$stored = intval($build);
if(! $stored) {
logger('Critical: check_config unable to determine database schema version');
return;
}
$current = intval($db_revision);
if($stored < $current) {
// The last update we performed was $stored.
// Start at $stored + 1 and continue until we have completed $current
for($x = $stored + 1; $x <= $current; $x ++) {
$s = '_' . $x;
$cls = '\\Zotlabs\Update\\' . $s ;
if(! class_exists($cls)) {
return;
}
// There could be a lot of processes running or about to run.
// We want exactly one process to run the update command.
// So store the fact that we're taking responsibility
// after first checking to see if somebody else already has.
// If the update fails or times-out completely you may need to
// delete the config entry to try again.
Config::Load('database');
if(get_config('database', $s))
break;
set_config('database',$s, '1');
$c = new $cls();
$retval = $c->run();
if($retval != UPDATE_SUCCESS) {
// Prevent sending hundreds of thousands of emails by creating
// a lockfile.
$lockfile = 'store/[data]/mailsent';
if ((file_exists($lockfile)) && (filemtime($lockfile) > (time() - 86400)))
return;
@unlink($lockfile);
//send the administrator an e-mail
file_put_contents($lockfile, $x);
$r = q("select account_language from account where account_email = '%s' limit 1",
dbesc(\App::$config['system']['admin_email'])
);
push_lang(($r) ? $r[0]['account_language'] : 'en');
z_mail(
[
'toEmail' => \App::$config['system']['admin_email'],
'messageSubject' => sprintf( t('Update Error at %s'), z_root()),
'textVersion' => replace_macros(get_intltext_template('update_fail_eml.tpl'),
[
'$sitename' => \App::$config['system']['sitename'],
'$siteurl' => z_root(),
'$update' => $x,
'$error' => sprintf( t('Update %s failed. See error logs.'), $x)
]
)
]
);
//try the logger
logger('CRITICAL: Update Failed: ' . $x);
pop_lang();
}
else {
set_config('database',$s, 'success');
}
}
}
set_config('system', 'db_version', $db_revision);
}
}
}

View File

@@ -63,13 +63,11 @@ class Enotify {
$thanks = t('Thank You,');
$sitename = get_config('system','sitename');
$site_admin = sprintf( t('%s Administrator'), $sitename);
$opt_out1 = sprintf( t('This email was sent by %1$s at %2$s.'), t('$Projectname'), \App::get_hostname());
$opt_out2 = sprintf( t('To stop receiving these messages, please adjust your Notification Settings at %s'), z_root() . '/settings');
$hopt_out2 = sprintf( t('To stop receiving these messages, please adjust your %s.'), '<a href="' . z_root() . '/settings' . '">' . t('Notification Settings') . '</a>');
$sender_name = $product;
$hostname = \App::get_hostname();
if(strpos($hostname,':'))
$hostname = substr($hostname,0,strpos($hostname,':'));
$hostname = substr($hostname,0,strpos($hostname,':'));
// Do not translate 'noreply' as it must be a legal 7-bit email address
@@ -79,13 +77,16 @@ class Enotify {
$sender_email = get_config('system','from_email');
if(! $sender_email)
$sender_email = 'Administrator' . '@' . $hostname;
$sender_email = 'Administrator' . '@' . \App::get_hostname();
$sender_name = get_config('system','from_email_name');
if(! $sender_name)
$sender_name = \Zotlabs\Lib\System::get_site_name();
$additional_mail_header = "";
if(array_key_exists('item', $params)) {
@@ -104,21 +105,12 @@ class Enotify {
$title = $params['item']['title'];
$body = $params['item']['body'];
}
if($params['item']['created'] < datetime_convert('UTC','UTC','now - 1 month')) {
logger('notification invoked for an old item which may have been refetched.',LOGGER_DEBUG,LOG_INFO);
return;
}
}
else {
$title = $body = '';
}
$always_show_in_notices = get_pconfig($recip['channel_id'],'system','always_show_in_notices');
$vnotify = get_pconfig($recip['channel_id'],'system','vnotify');
$salutation = $recip['channel_name'];
// e.g. "your post", "David's photo", etc.
$possess_desc = t('%s <!item_type!>');
@@ -126,7 +118,7 @@ class Enotify {
logger('notification: mail');
$subject = sprintf( t('[$Projectname:Notify] New mail received at %s'),$sitename);
$preamble = sprintf( t('%1$s sent you a new private message at %2$s.'), $sender['xchan_name'],$sitename);
$preamble = sprintf( t('%1$s, %2$s sent you a new private message at %3$s.'),$recip['channel_name'], $sender['xchan_name'],$sitename);
$epreamble = sprintf( t('%1$s sent you %2$s.'),'[zrl=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/zrl]', '[zrl=$itemlink]' . t('a private message') . '[/zrl]');
$sitelink = t('Please visit %s to view and/or reply to your private messages.');
$tsitelink = sprintf( $sitelink, $siteurl . '/mail/' . $params['item']['id'] );
@@ -135,28 +127,16 @@ class Enotify {
}
if ($params['type'] == NOTIFY_COMMENT) {
//logger("notification: params = " . print_r($params, true), LOGGER_DEBUG);
// logger("notification: params = " . print_r($params, true), LOGGER_DEBUG);
$moderated = (($params['item']['item_blocked'] == ITEM_MODERATED) ? true : false);
$itemlink = $params['link'];
$itemlink = $params['link'];
$action = t('commented on');
if(array_key_exists('item',$params) && in_array($params['item']['verb'], [ACTIVITY_LIKE, ACTIVITY_DISLIKE])) {
if(! $always_show_in_notices || !($vnotify & VNOTIFY_LIKE)) {
logger('notification: not a visible activity. Ignoring.');
pop_lang();
return;
}
if(activity_match($params['verb'], ACTIVITY_LIKE))
$action = t('liked');
if(activity_match($params['verb'], ACTIVITY_DISLIKE))
$action = t('disliked');
// ignore like/unlike activity on posts - they probably require a separate notification preference
if (array_key_exists('item',$params) && (! visible_activity($params['item']))) {
logger('notification: not a visible activity. Ignoring.');
pop_lang();
return;
}
$parent_mid = $params['parent_mid'];
@@ -189,6 +169,7 @@ class Enotify {
xchan_query($p);
$item_post_type = item_post_type($p[0]);
// $private = $p[0]['item_private'];
$parent_id = $p[0]['id'];
@@ -198,26 +179,26 @@ class Enotify {
//$possess_desc = str_replace('<!item_type!>',$possess_desc);
// "a post"
$dest_str = sprintf(t('%1$s %2$s [zrl=%3$s]a %4$s[/zrl]'),
$dest_str = sprintf(t('%1$s, %2$s commented on [zrl=%3$s]a %4$s[/zrl]'),
$recip['channel_name'],
'[zrl=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/zrl]',
$action,
$itemlink,
$item_post_type);
// "George Bull's post"
if($p)
$dest_str = sprintf(t('%1$s %2$s [zrl=%3$s]%4$s\'s %5$s[/zrl]'),
$dest_str = sprintf(t('%1$s, %2$s commented on [zrl=%3$s]%4$s\'s %5$s[/zrl]'),
$recip['channel_name'],
'[zrl=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/zrl]',
$action,
$itemlink,
$p[0]['author']['xchan_name'],
$item_post_type);
// "your post"
if($p[0]['owner']['xchan_name'] == $p[0]['author']['xchan_name'] && intval($p[0]['item_wall']))
$dest_str = sprintf(t('%1$s %2$s [zrl=%3$s]your %4$s[/zrl]'),
$dest_str = sprintf(t('%1$s, %2$s commented on [zrl=%3$s]your %4$s[/zrl]'),
$recip['channel_name'],
'[zrl=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/zrl]',
$action,
$itemlink,
$item_post_type);
@@ -226,92 +207,8 @@ class Enotify {
// Before this we have the name of the replier on the subject rendering
// differents subjects for messages on the same thread.
if($moderated)
$subject = sprintf( t('[$Projectname:Notify] Moderated Comment to conversation #%1$d by %2$s'), $parent_id, $sender['xchan_name']);
else
$subject = sprintf( t('[$Projectname:Notify] Comment to conversation #%1$d by %2$s'), $parent_id, $sender['xchan_name']);
$preamble = sprintf( t('%1$s commented on an item/conversation you have been following.'), $sender['xchan_name']);
$epreamble = $dest_str;
$sitelink = t('Please visit %s to view and/or reply to the conversation.');
$tsitelink = sprintf( $sitelink, $siteurl );
$hsitelink = sprintf( $sitelink, '<a href="' . $siteurl . '">' . $sitename . '</a>');
if($moderated) {
$tsitelink .= "\n\n" . sprintf( t('Please visit %s to approve or reject this comment.'), z_root() . '/moderate' );
$hsitelink .= "<br><br>" . sprintf( t('Please visit %s to approve or reject this comment.'), '<a href="' . z_root() . '/moderate">' . z_root() . '/moderate</a>' );
}
}
if ($params['type'] == NOTIFY_LIKE) {
// logger("notification: params = " . print_r($params, true), LOGGER_DEBUG);
$itemlink = $params['link'];
if (array_key_exists('item',$params) && (! activity_match($params['item']['verb'],ACTIVITY_LIKE))) {
if(! $always_show_in_notices || !($vnotify & VNOTIFY_LIKE)) {
logger('notification: not a visible activity. Ignoring.');
pop_lang();
return;
}
}
$parent_mid = $params['parent_mid'];
// Check to see if there was already a notify for this post.
// If so don't create a second notification
$p = null;
$p = q("select id from notify where link = '%s' and uid = %d limit 1",
dbesc($params['link']),
intval($recip['channel_id'])
);
if ($p) {
logger('notification: like already notified');
pop_lang();
return;
}
// if it's a post figure out who's post it is.
$p = null;
if($params['otype'] === 'item' && $parent_mid) {
$p = q("select * from item where mid = '%s' and uid = %d limit 1",
dbesc($parent_mid),
intval($recip['channel_id'])
);
}
xchan_query($p);
$item_post_type = item_post_type($p[0]);
// $private = $p[0]['item_private'];
$parent_id = $p[0]['id'];
$parent_item = $p[0];
// "your post"
if($p[0]['owner']['xchan_name'] == $p[0]['author']['xchan_name'] && intval($p[0]['item_wall']))
$dest_str = sprintf(t('%1$s liked [zrl=%2$s]your %3$s[/zrl]'),
'[zrl=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/zrl]',
$itemlink,
$item_post_type);
else {
pop_lang();
return;
}
// Some mail softwares relies on subject field for threading.
// So, we cannot have different subjects for notifications of the same thread.
// Before this we have the name of the replier on the subject rendering
// differents subjects for messages on the same thread.
$subject = sprintf( t('[$Projectname:Notify] Like received to conversation #%1$d by %2$s'), $parent_id, $sender['xchan_name']);
$preamble = sprintf( t('%1$s liked an item/conversation you created.'), $sender['xchan_name']);
$subject = sprintf( t('[$Projectname:Notify] Comment to conversation #%1$d by %2$s'), $parent_id, $sender['xchan_name']);
$preamble = sprintf( t('%1$s, %2$s commented on an item/conversation you have been following.'), $recip['channel_name'], $sender['xchan_name']);
$epreamble = $dest_str;
$sitelink = t('Please visit %s to view and/or reply to the conversation.');
@@ -319,14 +216,13 @@ class Enotify {
$hsitelink = sprintf( $sitelink, '<a href="' . $siteurl . '">' . $sitename . '</a>');
}
if($params['type'] == NOTIFY_WALL) {
$subject = sprintf( t('[$Projectname:Notify] %s posted to your profile wall') , $sender['xchan_name']);
$preamble = sprintf( t('%1$s posted to your profile wall at %2$s') , $sender['xchan_name'], $sitename);
$preamble = sprintf( t('%1$s, %2$s posted to your profile wall at %3$s') , $recip['channel_name'], $sender['xchan_name'], $sitename);
$epreamble = sprintf( t('%1$s posted to [zrl=%2$s]your wall[/zrl]') ,
$epreamble = sprintf( t('%1$s, %2$s posted to [zrl=%3$s]your wall[/zrl]') ,
$recip['channel_name'],
'[zrl=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/zrl]',
$params['link']);
@@ -350,8 +246,9 @@ class Enotify {
}
$subject = sprintf( t('[$Projectname:Notify] %s tagged you') , $sender['xchan_name']);
$preamble = sprintf( t('%1$s tagged you at %2$s') , $sender['xchan_name'], $sitename);
$epreamble = sprintf( t('%1$s [zrl=%2$s]tagged you[/zrl].') ,
$preamble = sprintf( t('%1$s, %2$s tagged you at %3$s') , $recip['channel_name'], $sender['xchan_name'], $sitename);
$epreamble = sprintf( t('%1$s, %2$s [zrl=%3$s]tagged you[/zrl].') ,
$recip['channel_name'],
'[zrl=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/zrl]',
$params['link']);
@@ -363,8 +260,9 @@ class Enotify {
if ($params['type'] == NOTIFY_POKE) {
$subject = sprintf( t('[$Projectname:Notify] %1$s poked you') , $sender['xchan_name']);
$preamble = sprintf( t('%1$s poked you at %2$s') , $sender['xchan_name'], $sitename);
$epreamble = sprintf( t('%1$s [zrl=%2$s]poked you[/zrl].') ,
$preamble = sprintf( t('%1$s, %2$s poked you at %3$s') , $recip['channel_name'], $sender['xchan_name'], $sitename);
$epreamble = sprintf( t('%1$s, %2$s [zrl=%2$s]poked you[/zrl].') ,
$recip['channel_name'],
'[zrl=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/zrl]',
$params['link']);
@@ -380,8 +278,9 @@ class Enotify {
if ($params['type'] == NOTIFY_TAGSHARE) {
$subject = sprintf( t('[$Projectname:Notify] %s tagged your post') , $sender['xchan_name']);
$preamble = sprintf( t('%1$s tagged your post at %2$s'),$sender['xchan_name'], $sitename);
$epreamble = sprintf( t('%1$s tagged [zrl=%2$s]your post[/zrl]') ,
$preamble = sprintf( t('%1$s, %2$s tagged your post at %3$s') , $recip['channel_name'],$sender['xchan_name'], $sitename);
$epreamble = sprintf( t('%1$s, %2$s tagged [zrl=%3$s]your post[/zrl]') ,
$recip['channel_name'],
'[zrl=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/zrl]',
$itemlink);
@@ -393,8 +292,9 @@ class Enotify {
if ($params['type'] == NOTIFY_INTRO) {
$subject = sprintf( t('[$Projectname:Notify] Introduction received'));
$preamble = sprintf( t('You\'ve received an new connection request from \'%1$s\' at %2$s'), $sender['xchan_name'], $sitename);
$epreamble = sprintf( t('You\'ve received [zrl=%1$s]a new connection request[/zrl] from %2$s.'),
$preamble = sprintf( t('%1$s, you\'ve received an new connection request from \'%2$s\' at %3$s'), $recip['channel_name'], $sender['xchan_name'], $sitename);
$epreamble = sprintf( t('%1$s, you\'ve received [zrl=%2$s]a new connection request[/zrl] from %3$s.'),
$recip['channel_name'],
$siteurl . '/connections/ifpending',
'[zrl=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/zrl]');
$body = sprintf( t('You may visit their profile at %s'),$sender['xchan_url']);
@@ -407,8 +307,9 @@ class Enotify {
if ($params['type'] == NOTIFY_SUGGEST) {
$subject = sprintf( t('[$Projectname:Notify] Friend suggestion received'));
$preamble = sprintf( t('You\'ve received a friend suggestion from \'%1$s\' at %2$s'), $sender['xchan_name'], $sitename);
$epreamble = sprintf( t('You\'ve received [zrl=%1$s]a friend suggestion[/zrl] for %2$s from %3$s.'),
$preamble = sprintf( t('%1$s, you\'ve received a friend suggestion from \'%2$s\' at %3$s'), $recip['channel_name'], $sender['xchan_name'], $sitename);
$epreamble = sprintf( t('%1$s, you\'ve received [zrl=%2$s]a friend suggestion[/zrl] for %3$s from %4$s.'),
$recip['channel_name'],
$itemlink,
'[zrl=' . $params['item']['url'] . ']' . $params['item']['name'] . '[/zrl]',
'[zrl=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/zrl]');
@@ -463,7 +364,7 @@ class Enotify {
do {
$dups = false;
$hash = random_string();
$r = q("SELECT id FROM notify WHERE hash = '%s' LIMIT 1",
$r = q("SELECT `id` FROM `notify` WHERE `hash` = '%s' LIMIT 1",
dbesc($hash));
if ($r)
$dups = true;
@@ -506,20 +407,21 @@ class Enotify {
// Another option would be to not add them to the DB, and change how emails are handled
// (probably would be better that way)
$always_show_in_notices = get_pconfig($recip['channel_id'],'system','always_show_in_notices');
if (!$always_show_in_notices) {
if (($params['type'] == NOTIFY_WALL) || ($params['type'] == NOTIFY_MAIL) || ($params['type'] == NOTIFY_INTRO)) {
$seen = 1;
}
}
$r = q("insert into notify (hash,xname,url,photo,created,msg,aid,uid,link,parent,seen,ntype,verb,otype)
values('%s','%s','%s','%s','%s','%s',%d,%d,'%s','%s',%d,%d,'%s','%s')",
$r = q("insert into notify (hash,xname,url,photo,created,aid,uid,link,parent,seen,ntype,verb,otype)
values('%s','%s','%s','%s','%s',%d,%d,'%s','%s',%d,%d,'%s','%s')",
dbesc($datarray['hash']),
dbesc($datarray['xname']),
dbesc($datarray['url']),
dbesc($datarray['photo']),
dbesc($datarray['created']),
dbesc(''), // will fill this in below after the record is created
intval($datarray['aid']),
intval($datarray['uid']),
dbesc($datarray['link']),
@@ -608,9 +510,6 @@ class Enotify {
$datarray['titemlink'] = $itemlink;
$datarray['thanks'] = $thanks;
$datarray['site_admin'] = $site_admin;
$datarray['opt_out1'] = $opt_out1;
$datarray['opt_out2'] = $opt_out2;
$datarray['hopt_out2'] = $hopt_out2;
$datarray['title'] = stripslashes($title);
$datarray['htmlversion'] = $htmlversion;
$datarray['textversion'] = $textversion;
@@ -657,7 +556,7 @@ class Enotify {
'$banner' => $datarray['banner'],
'$notify_icon' => \Zotlabs\Lib\System::get_notify_icon(),
'$product' => $datarray['product'],
'$preamble' => $salutation . '<br><br>' . $datarray['preamble'],
'$preamble' => $datarray['preamble'],
'$sitename' => $datarray['sitename'],
'$siteurl' => $datarray['siteurl'],
'$source_name' => $datarray['source_name'],
@@ -668,8 +567,6 @@ class Enotify {
'$hitemlink' => $datarray['hitemlink'],
'$thanks' => $datarray['thanks'],
'$site_admin' => $datarray['site_admin'],
'$opt_out1' => $datarray['opt_out1'],
'$opt_out2' => $datarray['hopt_out2'],
'$title' => $datarray['title'],
'$htmlversion' => $datarray['htmlversion'],
));
@@ -679,7 +576,7 @@ class Enotify {
$email_text_body = replace_macros($tpl, array(
'$banner' => $datarray['banner'],
'$product' => $datarray['product'],
'$preamble' => $salutation . "\n\n" . $datarray['preamble'],
'$preamble' => $datarray['preamble'],
'$sitename' => $datarray['sitename'],
'$siteurl' => $datarray['siteurl'],
'$source_name' => $datarray['source_name'],
@@ -690,8 +587,6 @@ class Enotify {
'$titemlink' => $datarray['titemlink'],
'$thanks' => $datarray['thanks'],
'$site_admin' => $datarray['site_admin'],
'$opt_out1' => $datarray['opt_out1'],
'$opt_out2' => $datarray['opt_out2'],
'$title' => $datarray['title'],
'$textversion' => $datarray['textversion'],
));
@@ -738,7 +633,7 @@ class Enotify {
call_hooks('email_send', $params);
if($params['sent']) {
logger("notification: enotify::send (addon) returns " . (($params['result']) ? 'success' : 'failure'), LOGGER_DEBUG);
logger("notification: enotify::send (addon) returns " . $params['result'], LOGGER_DEBUG);
return $params['result'];
}
@@ -747,8 +642,8 @@ class Enotify {
// generate a mime boundary
$mimeBoundary = rand(0, 9) . "-"
.rand(100000000, 999999999) . "-"
.rand(100000000, 999999999) . "=:"
.rand(10000000000, 9999999999) . "-"
.rand(10000000000, 9999999999) . "=:"
.rand(10000, 99999);
// generate a multipart/alternative message header
@@ -781,7 +676,7 @@ class Enotify {
$multipartMessageBody, // message body
$messageHeader // message headers
);
logger("notification: enotify::send returns " . (($res) ? 'success' : 'failure'), LOGGER_DEBUG);
logger("notification: enotify::send returns " . $res, LOGGER_DEBUG);
return $res;
}
@@ -793,14 +688,10 @@ class Enotify {
// Call localize_item to get a one line status for activities.
// This should set $item['localized'] to indicate we have a brief summary.
// and perhaps $item['shortlocalized'] for an even briefer summary
localize_item($item);
if($item['shortlocalize']) {
$itemem_text = $item['shortlocalize'];
}
elseif($item['localize']) {
if($item['localize']) {
$itemem_text = $item['localize'];
}
else {
@@ -809,20 +700,6 @@ class Enotify {
: sprintf( t('commented on %s\'s post'), $item['owner']['xchan_name']));
}
$edit = false;
if($item['edited'] > $item['created']) {
if($item['item_thread_top']) {
$itemem_text = sprintf( t('edited a post dated %s'), relative_date($item['created']));
$edit = true;
}
else {
$itemem_text = sprintf( t('edited a comment dated %s'), relative_date($item['created']));
$edit = true;
}
}
// convert this logic into a json array just like the system notifications
return array(
@@ -830,11 +707,8 @@ class Enotify {
'name' => $item['author']['xchan_name'],
'url' => $item['author']['xchan_url'],
'photo' => $item['author']['xchan_photo_s'],
'when' => relative_date(($edit)? $item['edited'] : $item['created']),
'class' => (intval($item['item_unseen']) ? 'notify-unseen' : 'notify-seen'),
'b64mid' => ((in_array($item['verb'], [ACTIVITY_LIKE, ACTIVITY_DISLIKE])) ? 'b64.' . base64url_encode($item['thr_parent']) : 'b64.' . base64url_encode($item['mid'])),
'notify_id' => 'undefined',
'thread_top' => (($item['item_thread_top']) ? true : false),
'when' => relative_date($item['created']),
'class' => (intval($item['item_unseen']) ? 'notify-unseen' : 'notify-seen'),
'message' => strip_tags(bbcode($itemem_text))
);

View File

@@ -10,7 +10,7 @@ class IConfig {
return;
}
static public function Get(&$item, $family, $key, $default = false) {
static public function Get(&$item, $family, $key) {
$is_item = false;
@@ -28,7 +28,7 @@ class IConfig {
$iid = $item;
if(! $iid)
return $default;
return false;
if(is_array($item) && array_key_exists('iconfig',$item) && is_array($item['iconfig'])) {
foreach($item['iconfig'] as $c) {
@@ -48,7 +48,7 @@ class IConfig {
$item['iconfig'][] = $r[0];
return $r[0]['v'];
}
return $default;
return false;
}

View File

@@ -1,122 +0,0 @@
<?php
namespace Zotlabs\Lib;
class Img_filesize {
private $url;
function __construct($url) {
$this->url = $url;
}
function getSize() {
$size = null;
if(stripos($this->url,z_root() . '/photo') !== false) {
$size = self::getLocalFileSize($this->url);
}
if(! $size) {
$size = getRemoteFileSize($this->url);
}
return $size;
}
static function getLocalFileSize($url) {
$fname = basename($url);
$resolution = 0;
if(strpos($fname,'.') !== false)
$fname = substr($fname,0,strpos($fname,'.'));
if(substr($fname,-2,1) == '-') {
$resolution = intval(substr($fname,-1,1));
$fname = substr($fname,0,-2);
}
$r = q("SELECT filesize FROM photo WHERE resource_id = '%s' AND imgscale = %d LIMIT 1",
dbesc($fname),
intval($resolution)
);
if($r) {
return $r[0]['filesize'];
}
return null;
}
}
/**
* Try to determine the size of a remote file by making an HTTP request for
* a byte range, or look for the content-length header in the response.
* The function aborts the transfer as soon as the size is found, or if no
* length headers are returned, it aborts the transfer.
*
* @return int|null null if size could not be determined, or length of content
*/
function getRemoteFileSize($url)
{
$ch = curl_init($url);
$headers = array(
'Range: bytes=0-1',
'Connection: close',
);
$in_headers = true;
$size = null;
curl_setopt($ch, CURLOPT_HEADER, 1);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2450.0 Iron/46.0.2450.0');
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
curl_setopt($ch, CURLOPT_VERBOSE, 0); // set to 1 to debug
curl_setopt($ch, CURLOPT_STDERR, fopen('php://output', 'r'));
curl_setopt($ch, CURLOPT_HEADERFUNCTION, function($curl, $line) use (&$in_headers, &$size) {
$length = strlen($line);
if (trim($line) == '') {
$in_headers = false;
}
list($header, $content) = explode(':', $line, 2);
$header = strtolower(trim($header));
if ($header == 'content-range') {
// found a content-range header
list($rng, $s) = explode('/', $content, 2);
$size = (int)$s;
return 0; // aborts transfer
} else if ($header == 'content-length' && 206 != curl_getinfo($curl, CURLINFO_HTTP_CODE)) {
// found content-length header and this is not a 206 Partial Content response (range response)
$size = (int)$content;
return 0;
} else {
// continue
return $length;
}
});
curl_setopt($ch, CURLOPT_WRITEFUNCTION, function($curl, $data) use ($in_headers) {
if (!$in_headers) {
// shouldn't be here unless we couldn't determine file size
// abort transfer
return 0;
}
// write function is also called when reading headers
return strlen($data);
});
curl_exec($ch);
curl_getinfo($ch);
curl_close($ch);
return $size;
}

View File

@@ -1,38 +0,0 @@
<?php
namespace Zotlabs\Lib;
class JSalmon {
static function sign($data,$key_id,$key) {
$arr = $data;
$data = json_encode($data,JSON_UNESCAPED_SLASHES);
$data = base64url_encode($data, false); // do not strip padding
$data_type = 'application/x-zot+json';
$encoding = 'base64url';
$algorithm = 'RSA-SHA256';
$data = preg_replace('/\s+/','',$data);
// precomputed base64url encoding of data_type, encoding, algorithm concatenated with periods
$precomputed = '.' . base64url_encode($data_type,false) . '.YmFzZTY0dXJs.UlNBLVNIQTI1Ng==';
$signature = base64url_encode(rsa_sign($data . $precomputed, $key), false);
return ([
'signed' => true,
'data' => $data,
'data_type' => $data_type,
'encoding' => $encoding,
'alg' => $algorithm,
'sigs' => [
'value' => $signature,
'key_id' => base64url_encode($key_id)
]
]);
}
}

View File

@@ -1,135 +0,0 @@
<?php
namespace Zotlabs\Lib;
require_once('library/jsonld/jsonld.php');
class LDSignatures {
static function verify($data,$pubkey) {
$ohash = self::hash(self::signable_options($data['signature']));
$dhash = self::hash(self::signable_data($data));
$x = rsa_verify($ohash . $dhash,base64_decode($data['signature']['signatureValue']), $pubkey);
logger('LD-verify: ' . intval($x));
return $x;
}
static function dopplesign(&$data,$channel) {
// remove for the time being - performance issues
// $data['magicEnv'] = self::salmon_sign($data,$channel);
return self::sign($data,$channel);
}
static function sign($data,$channel) {
$options = [
'type' => 'RsaSignature2017',
'nonce' => random_string(64),
'creator' => z_root() . '/channel/' . $channel['channel_address'] . '/public_key_pem',
'created' => datetime_convert('UTC','UTC', 'now', 'Y-m-d\Th:i:s\Z')
];
$ohash = self::hash(self::signable_options($options));
$dhash = self::hash(self::signable_data($data));
$options['signatureValue'] = base64_encode(rsa_sign($ohash . $dhash,$channel['channel_prvkey']));
$signed = array_merge([
'@context' => [
ACTIVITYSTREAMS_JSONLD_REV,
'https://w3id.org/security/v1' ],
],$options);
return $signed;
}
static function signable_data($data) {
$newdata = [];
if($data) {
foreach($data as $k => $v) {
if(! in_array($k,[ 'signature' ])) {
$newdata[$k] = $v;
}
}
}
return json_encode($newdata,JSON_UNESCAPED_SLASHES);
}
static function signable_options($options) {
$newopts = [ '@context' => 'https://w3id.org/identity/v1' ];
if($options) {
foreach($options as $k => $v) {
if(! in_array($k,[ 'type','id','signatureValue' ])) {
$newopts[$k] = $v;
}
}
}
return json_encode($newopts,JSON_UNESCAPED_SLASHES);
}
static function hash($obj) {
return hash('sha256',self::normalise($obj));
}
static function normalise($data) {
if(is_string($data)) {
$data = json_decode($data);
}
if(! is_object($data))
return '';
jsonld_set_document_loader('jsonld_document_loader');
try {
$d = jsonld_normalize($data,[ 'algorithm' => 'URDNA2015', 'format' => 'application/nquads' ]);
}
catch (\Exception $e) {
logger('normalise error:' . print_r($e,true));
logger('normalise error: ' . print_r($data,true));
}
return $d;
}
static function salmon_sign($data,$channel) {
$arr = $data;
$data = json_encode($data,JSON_UNESCAPED_SLASHES);
$data = base64url_encode($data, false); // do not strip padding
$data_type = 'application/activity+json';
$encoding = 'base64url';
$algorithm = 'RSA-SHA256';
$keyhash = base64url_encode(z_root() . '/channel/' . $channel['channel_address']);
$data = str_replace(array(" ","\t","\r","\n"),array("","","",""),$data);
// precomputed base64url encoding of data_type, encoding, algorithm concatenated with periods
$precomputed = '.' . base64url_encode($data_type,false) . '.YmFzZTY0dXJs.UlNBLVNIQTI1Ng==';
$signature = base64url_encode(rsa_sign($data . $precomputed,$channel['channel_prvkey']));
return ([
'id' => $arr['id'],
'meData' => $data,
'meDataType' => $data_type,
'meEncoding' => $encoding,
'meAlgorithm' => $algorithm,
'meCreator' => z_root() . '/channel/' . $channel['channel_address'] . '/public_key_pem',
'meSignatureValue' => $signature
]);
}
}

View File

@@ -1,132 +0,0 @@
<?php
namespace Zotlabs\Lib;
/**
* @brief MarkdownSoap class.
*
* Purify Markdown for storage
* @code{.php}
* $x = new MarkdownSoap($string_to_be_cleansed);
* $text = $x->clean();
* @endcode
* What this does:
* 1. extracts code blocks and privately escapes them from processing
* 2. Run html purifier on the content
* 3. put back the code blocks
* 4. run htmlspecialchars on the entire content for safe storage
*
* At render time:
* @code{.php}
* $markdown = \Zotlabs\Lib\MarkdownSoap::unescape($text);
* $html = \Michelf\MarkdownExtra::DefaultTransform($markdown);
* @endcode
*/
class MarkdownSoap {
/**
* @var string
*/
private $str;
/**
* @var string
*/
private $token;
function __construct($s) {
$this->str = $s;
$this->token = random_string(20);
}
function clean() {
$x = $this->extract_code($this->str);
$x = $this->purify($x);
$x = $this->putback_code($x);
$x = $this->escape($x);
return $x;
}
/**
* @brief Extracts code blocks and privately escapes them from processing.
*
* @see encode_code()
* @see putback_code()
*
* @param string $s
* @return string
*/
function extract_code($s) {
$text = preg_replace_callback('{
(?:\n\n|\A\n?)
( # $1 = the code block -- one or more lines, starting with a space/tab
(?>
[ ]{'.'4'.'} # Lines must start with a tab or a tab-width of spaces
.*\n+
)+
)
((?=^[ ]{0,'.'4'.'}\S)|\Z) # Lookahead for non-space at line-start, or end of doc
}xm',
[ $this , 'encode_code' ], $s);
return $text;
}
function encode_code($matches) {
return $this->token . ';' . base64_encode($matches[0]) . ';' ;
}
function decode_code($matches) {
return base64_decode($matches[1]);
}
/**
* @brief Put back the code blocks.
*
* @see extract_code()
* @see decode_code()
*
* @param string $s
* @return string
*/
function putback_code($s) {
$text = preg_replace_callback('{' . $this->token . '\;(.*?)\;}xm', [ $this, 'decode_code' ], $s);
return $text;
}
function purify($s) {
$s = $this->protect_autolinks($s);
$s = purify_html($s);
$s = $this->unprotect_autolinks($s);
return $s;
}
function protect_autolinks($s) {
$s = preg_replace('/\<(https?\:\/\/)(.*?)\>/', '[$1$2]($1$2)', $s);
return $s;
}
function unprotect_autolinks($s) {
return $s;
}
function escape($s) {
return htmlspecialchars($s, ENT_QUOTES, 'UTF-8', false);
}
/**
* @brief Converts special HTML entities back to characters.
*
* @param string $s
* @return string
*/
static public function unescape($s) {
return htmlspecialchars_decode($s, ENT_QUOTES);
}
}

View File

@@ -1,79 +0,0 @@
<?php
namespace Zotlabs\Lib;
class MessageFilter {
static public function evaluate($item,$incl,$excl) {
require_once('include/html2plain.php');
unobscure($item);
$text = prepare_text($item['body'],$item['mimetype']);
$text = html2plain(($item['title']) ? $item['title'] . ' ' . $text : $text);
$lang = null;
if((strpos($incl,'lang=') !== false) || (strpos($excl,'lang=') !== false)) {
$lang = detect_language($text);
}
$tags = ((is_array($item['term']) && count($item['term'])) ? $item['term'] : false);
// exclude always has priority
$exclude = (($excl) ? explode("\n",$excl) : null);
if($exclude) {
foreach($exclude as $word) {
$word = trim($word);
if(! $word)
continue;
if(substr($word,0,1) === '#' && $tags) {
foreach($tags as $t)
if((($t['ttype'] == TERM_HASHTAG) || ($t['ttype'] == TERM_COMMUNITYTAG)) && (($t['term'] === substr($word,1)) || (substr($word,1) === '*')))
return false;
}
elseif((strpos($word,'/') === 0) && preg_match($word,$text))
return false;
elseif((strpos($word,'lang=') === 0) && ($lang) && (strcasecmp($lang,trim(substr($word,5))) == 0))
return false;
elseif(stristr($text,$word) !== false)
return false;
}
}
$include = (($incl) ? explode("\n",$incl) : null);
if($include) {
foreach($include as $word) {
$word = trim($word);
if(! $word)
continue;
if(substr($word,0,1) === '#' && $tags) {
foreach($tags as $t)
if((($t['ttype'] == TERM_HASHTAG) || ($t['ttype'] == TERM_COMMUNITYTAG)) && (($t['term'] === substr($word,1)) || (substr($word,1) === '*')))
return true;
}
elseif((strpos($word,'/') === 0) && preg_match($word,$text))
return true;
elseif((strpos($word,'lang=') === 0) && ($lang) && (strcasecmp($lang,trim(substr($word,5))) == 0))
return true;
elseif(stristr($text,$word) !== false)
return true;
}
}
else {
return true;
}
return false;
}
}

View File

@@ -1,289 +0,0 @@
<?php
namespace Zotlabs\Lib;
define ( 'NWIKI_ITEM_RESOURCE_TYPE', 'nwiki' );
class NativeWiki {
static public function listwikis($channel, $observer_hash) {
$sql_extra = item_permissions_sql($channel['channel_id'], $observer_hash);
$wikis = q("SELECT * FROM item
WHERE resource_type = '%s' AND mid = parent_mid AND uid = %d AND item_deleted = 0 $sql_extra",
dbesc(NWIKI_ITEM_RESOURCE_TYPE),
intval($channel['channel_id'])
);
if($wikis) {
foreach($wikis as &$w) {
$w['json_allow_cid'] = acl2json($w['allow_cid']);
$w['json_allow_gid'] = acl2json($w['allow_gid']);
$w['json_deny_cid'] = acl2json($w['deny_cid']);
$w['json_deny_gid'] = acl2json($w['deny_gid']);
$w['rawName'] = get_iconfig($w, 'wiki', 'rawName');
$w['htmlName'] = escape_tags($w['rawName']);
$w['urlName'] = urlencode(urlencode($w['rawName']));
$w['mimeType'] = get_iconfig($w, 'wiki', 'mimeType');
$w['typelock'] = get_iconfig($w, 'wiki', 'typelock');
$w['lockstate'] = (($w['allow_cid'] || $w['allow_gid'] || $w['deny_cid'] || $w['deny_gid']) ? 'lock' : 'unlock');
}
}
// TODO: query db for wikis the observer can access. Return with two lists, for read and write access
return array('wikis' => $wikis);
}
function create_wiki($channel, $observer_hash, $wiki, $acl) {
// Generate unique resource_id using the same method as item_message_id()
do {
$dups = false;
$resource_id = random_string();
$r = q("SELECT mid FROM item WHERE resource_id = '%s' AND resource_type = '%s' AND uid = %d LIMIT 1",
dbesc($resource_id),
dbesc(NWIKI_ITEM_RESOURCE_TYPE),
intval($channel['channel_id'])
);
if($r)
$dups = true;
} while($dups == true);
$ac = $acl->get();
$mid = item_message_id();
$arr = array(); // Initialize the array of parameters for the post
$item_hidden = ((intval($wiki['postVisible']) === 0) ? 1 : 0);
$wiki_url = z_root() . '/wiki/' . $channel['channel_address'] . '/' . $wiki['urlName'];
$arr['aid'] = $channel['channel_account_id'];
$arr['uid'] = $channel['channel_id'];
$arr['mid'] = $mid;
$arr['parent_mid'] = $mid;
$arr['item_hidden'] = $item_hidden;
$arr['resource_type'] = NWIKI_ITEM_RESOURCE_TYPE;
$arr['resource_id'] = $resource_id;
$arr['owner_xchan'] = $channel['channel_hash'];
$arr['author_xchan'] = $observer_hash;
$arr['plink'] = z_root() . '/channel/' . $channel['channel_address'] . '/?f=&mid=' . urlencode($arr['mid']);
$arr['llink'] = $arr['plink'];
$arr['title'] = $wiki['htmlName']; // name of new wiki;
$arr['allow_cid'] = $ac['allow_cid'];
$arr['allow_gid'] = $ac['allow_gid'];
$arr['deny_cid'] = $ac['deny_cid'];
$arr['deny_gid'] = $ac['deny_gid'];
$arr['item_wall'] = 1;
$arr['item_origin'] = 1;
$arr['item_thread_top'] = 1;
$arr['item_private'] = intval($acl->is_private());
$arr['verb'] = ACTIVITY_CREATE;
$arr['obj_type'] = ACTIVITY_OBJ_WIKI;
$arr['body'] = '[table][tr][td][h1]New Wiki[/h1][/td][/tr][tr][td][zrl=' . $wiki_url . ']' . $wiki['htmlName'] . '[/zrl][/td][/tr][/table]';
$arr['public_policy'] = map_scope(\Zotlabs\Access\PermissionLimits::Get($channel['channel_id'],'view_wiki'),true);
// Save the wiki name information using iconfig. This is shareable.
if(! set_iconfig($arr, 'wiki', 'rawName', $wiki['rawName'], true)) {
return array('item' => null, 'success' => false);
}
if(! set_iconfig($arr, 'wiki', 'mimeType', $wiki['mimeType'], true)) {
return array('item' => null, 'success' => false);
}
set_iconfig($arr,'wiki','typelock',$wiki['typelock'],true);
$post = item_store($arr);
$item_id = $post['item_id'];
if($item_id) {
\Zotlabs\Daemon\Master::Summon(array('Notifier', 'activity', $item_id));
return array('item' => $post['item'], 'item_id' => $item_id, 'success' => true);
}
else {
return array('item' => null, 'success' => false);
}
}
function update_wiki($channel_id, $observer_hash, $arr, $acl) {
$w = self::get_wiki($channel_id, $observer_hash, $arr['resource_id']);
$item = $w['wiki'];
if(! $item) {
return array('item' => null, 'success' => false);
}
$x = $acl->get();
$item['allow_cid'] = $x['allow_cid'];
$item['allow_gid'] = $x['allow_gid'];
$item['deny_cid'] = $x['deny_cid'];
$item['deny_gid'] = $x['deny_gid'];
$item['item_private'] = intval($acl->is_private());
$update_title = false;
if($item['title'] !== $arr['updateRawName']) {
$update_title = true;
$item['title'] = $arr['updateRawName'];
}
$update = item_store_update($item);
$item_id = $update['item_id'];
// update acl for any existing wiki pages
q("update item set allow_cid = '%s', allow_gid = '%s', deny_cid = '%s', deny_gid = '%s', item_private = %d where resource_type = 'nwikipage' and resource_id = '%s'",
dbesc($item['allow_cid']),
dbesc($item['allow_gid']),
dbesc($item['deny_cid']),
dbesc($item['deny_gid']),
dbesc($item['item_private']),
dbesc($arr['resource_id'])
);
if($update['item_id']) {
info( t('Wiki updated successfully'));
if($update_title) {
// Update the wiki name information using iconfig.
if(! set_iconfig($update['item_id'], 'wiki', 'rawName', $arr['updateRawName'], true)) {
return array('item' => null, 'success' => false);
}
}
return array('item' => $update['item'], 'item_id' => $update['item_id'], 'success' => $update['success']);
}
else {
return array('item' => null, 'success' => false);
}
}
static public function sync_a_wiki_item($uid,$id,$resource_id) {
$r = q("SELECT * from item WHERE uid = %d AND ( id = %d OR ( resource_type = '%s' and resource_id = '%s' )) ",
intval($uid),
intval($id),
dbesc(NWIKI_ITEM_RESOURCE_TYPE),
dbesc($resource_id)
);
if($r) {
$q = q("select * from item where resource_type = 'nwikipage' and resource_id = '%s'",
dbesc($r[0]['resource_id'])
);
if($q) {
$r = array_merge($r,$q);
}
xchan_query($r);
$sync_item = fetch_post_tags($r);
if($sync_item) {
$pkt = [];
foreach($sync_item as $w) {
$pkt[] = encode_item($w,true);
}
build_sync_packet($uid,array('wiki' => $pkt));
}
}
}
function delete_wiki($channel_id,$observer_hash,$resource_id) {
$w = self::get_wiki($channel_id,$observer_hash,$resource_id);
$item = $w['wiki'];
if(! $item) {
return array('item' => null, 'success' => false);
}
else {
$drop = drop_item($item['id'], false, DROPITEM_NORMAL, true);
}
info( t('Wiki files deleted successfully'));
return array('item' => $item, 'item_id' => $item['id'], 'success' => (($drop === 1) ? true : false));
}
static public function get_wiki($channel_id, $observer_hash, $resource_id) {
$sql_extra = item_permissions_sql($channel_id,$observer_hash);
$item = q("SELECT * FROM item WHERE uid = %d AND resource_type = '%s' AND resource_id = '%s' AND item_deleted = 0
$sql_extra limit 1",
intval($channel_id),
dbesc(NWIKI_ITEM_RESOURCE_TYPE),
dbesc($resource_id)
);
if(! $item) {
return array('wiki' => null);
}
else {
$w = $item[0]; // wiki item table record
// Get wiki metadata
$rawName = get_iconfig($w, 'wiki', 'rawName');
$mimeType = get_iconfig($w, 'wiki', 'mimeType');
$typelock = get_iconfig($w, 'wiki', 'typelock');
return array(
'wiki' => $w,
'rawName' => $rawName,
'htmlName' => escape_tags($rawName),
'urlName' => urlencode(urlencode($rawName)),
'mimeType' => $mimeType,
'typelock' => $typelock
);
}
}
static public function exists_by_name($uid, $urlName) {
$sql_extra = item_permissions_sql($uid);
$item = q("SELECT item.id, resource_id FROM item left join iconfig on iconfig.iid = item.id
WHERE resource_type = '%s' AND iconfig.v = '%s' AND uid = %d
AND item_deleted = 0 $sql_extra limit 1",
dbesc(NWIKI_ITEM_RESOURCE_TYPE),
dbesc(urldecode($urlName)),
intval($uid)
);
if($item) {
return array('id' => $item[0]['id'], 'resource_id' => $item[0]['resource_id']);
}
else {
return array('id' => null, 'resource_id' => null);
}
}
static public function get_permissions($resource_id, $owner_id, $observer_hash) {
// TODO: For now, only the owner can edit
$sql_extra = item_permissions_sql($owner_id, $observer_hash);
if(local_channel() && local_channel() == $owner_id) {
return [ 'read' => true, 'write' => true, 'success' => true ];
}
$r = q("SELECT * FROM item WHERE uid = %d and resource_type = '%s' AND resource_id = '%s' $sql_extra LIMIT 1",
intval($owner_id),
dbesc(NWIKI_ITEM_RESOURCE_TYPE),
dbesc($resource_id)
);
if(! $r) {
return array('read' => false, 'write' => false, 'success' => true);
}
else {
// TODO: Create a new permission setting for wiki analogous to webpages. Until
// then, use webpage permissions
$write = perm_is_allowed($owner_id, $observer_hash,'write_wiki');
return array('read' => true, 'write' => $write, 'success' => true);
}
}
}

View File

@@ -1,695 +0,0 @@
<?php
namespace Zotlabs\Lib;
use \Zotlabs\Lib as Zlib;
class NativeWikiPage {
static public function page_list($channel_id,$observer_hash, $resource_id) {
// TODO: Create item table records for pages so that metadata like title can be applied
$w = Zlib\NativeWiki::get_wiki($channel_id,$observer_hash,$resource_id);
$pages[] = [
'resource_id' => '',
'title' => 'Home',
'url' => 'Home',
'link_id' => 'id_wiki_home_0'
];
$sql_extra = item_permissions_sql($channel_id,$observer_hash);
$r = q("select * from item where resource_type = 'nwikipage' and resource_id = '%s' and uid = %d and item_deleted = 0
$sql_extra order by title asc",
dbesc($resource_id),
intval($channel_id)
);
if($r) {
$x = [];
$y = [];
foreach($r as $rv) {
if(! in_array($rv['mid'],$x)) {
$y[] = $rv;
$x[] = $rv['mid'];
}
}
$items = fetch_post_tags($y,true);
foreach($items as $page_item) {
$title = get_iconfig($page_item['id'],'nwikipage','pagetitle',t('(No Title)'));
if(urldecode($title) !== 'Home') {
$pages[] = [
'resource_id' => $resource_id,
'title' => escape_tags($title),
'url' => str_replace('%2F','/',urlencode(str_replace('%2F','/',urlencode($title)))),
'link_id' => 'id_' . substr($resource_id, 0, 10) . '_' . $page_item['id']
];
}
}
}
return array('pages' => $pages, 'wiki' => $w);
}
static public function create_page($channel_id, $observer_hash, $name, $resource_id, $mimetype = 'text/bbcode') {
logger('mimetype: ' . $mimetype);
if(! in_array($mimetype,[ 'text/markdown','text/bbcode','text/plain','text/html' ]))
$mimetype = 'text/markdown';
$w = Zlib\NativeWiki::get_wiki($channel_id, $observer_hash, $resource_id);
if (! $w['wiki']) {
return array('content' => null, 'message' => 'Error reading wiki', 'success' => false);
}
// backslashes won't work well in the javascript functions
$name = str_replace('\\','',$name);
// create an empty activity
$arr = [];
$arr['uid'] = $channel_id;
$arr['author_xchan'] = $observer_hash;
$arr['mimetype'] = $mimetype;
$arr['title'] = $name;
$arr['resource_type'] = 'nwikipage';
$arr['resource_id'] = $resource_id;
$arr['allow_cid'] = $w['wiki']['allow_cid'];
$arr['allow_gid'] = $w['wiki']['allow_gid'];
$arr['deny_cid'] = $w['wiki']['deny_cid'];
$arr['deny_gid'] = $w['wiki']['deny_gid'];
$arr['public_policy'] = map_scope(\Zotlabs\Access\PermissionLimits::Get($channel_id,'view_wiki'),true);
// We may wish to change this some day.
$arr['item_unpublished'] = 1;
set_iconfig($arr,'nwikipage','pagetitle',(($name) ? $name : t('(No Title)')),true);
$p = post_activity_item($arr, false, false);
if($p['item_id']) {
$page = [
'rawName' => $name,
'htmlName' => escape_tags($name),
'urlName' => urlencode($name),
];
return array('page' => $page, 'item_id' => $p['item_id'], 'item' => $p['activity'], 'wiki' => $w, 'message' => '', 'success' => true);
}
return [ 'success' => false, 'message' => t('Wiki page create failed.') ];
}
static public function rename_page($arr) {
$pageUrlName = ((array_key_exists('pageUrlName',$arr)) ? $arr['pageUrlName'] : '');
$pageNewName = ((array_key_exists('pageNewName',$arr)) ? $arr['pageNewName'] : '');
$resource_id = ((array_key_exists('resource_id',$arr)) ? $arr['resource_id'] : '');
$observer_hash = ((array_key_exists('observer_hash',$arr)) ? $arr['observer_hash'] : '');
$channel_id = ((array_key_exists('channel_id',$arr)) ? $arr['channel_id'] : 0);
$w = Zlib\NativeWiki::get_wiki($channel_id, $observer_hash, $resource_id);
if(! $w['wiki']) {
return array('message' => t('Wiki not found.'), 'success' => false);
}
$ic = q("select * from iconfig left join item on iconfig.iid = item.id
where uid = %d and cat = 'nwikipage' and k = 'pagetitle' and v = '%s'",
intval($channel_id),
dbesc($pageNewName)
);
if($ic) {
return [ 'success' => false, 'message' => t('Destination name already exists') ];
}
$ids = [];
$ic = q("select *, item.id as item_id from iconfig left join item on iconfig.iid = item.id
where uid = %d and cat = 'nwikipage' and k = 'pagetitle' and v = '%s'",
intval($channel_id),
dbesc($pageUrlName)
);
if($ic) {
foreach($ic as $c) {
set_iconfig($c['item_id'],'nwikipage','pagetitle',$pageNewName);
$ids[] = $c['item_id'];
}
$str_ids = implode(',', $ids);
q("update item set title = '%s' where id in ($str_ids)",
dbesc($pageNewName)
);
$page = [
'rawName' => $pageNewName,
'htmlName' => escape_tags($pageNewName),
'urlName' => urlencode(escape_tags($pageNewName))
];
return [ 'success' => true, 'page' => $page ];
}
return [ 'success' => false, 'item_id' => $c['item_id'], 'message' => t('Page not found') ];
}
static public function get_page_content($arr) {
$pageUrlName = ((array_key_exists('pageUrlName',$arr)) ? $arr['pageUrlName'] : '');
$resource_id = ((array_key_exists('resource_id',$arr)) ? $arr['resource_id'] : '');
$observer_hash = ((array_key_exists('observer_hash',$arr)) ? $arr['observer_hash'] : '');
$channel_id = ((array_key_exists('channel_id',$arr)) ? intval($arr['channel_id']) : 0);
$revision = ((array_key_exists('revision',$arr)) ? intval($arr['revision']) : (-1));
$w = Zlib\NativeWiki::get_wiki($channel_id, $observer_hash, $resource_id);
if (! $w['wiki']) {
return array('content' => null, 'message' => 'Error reading wiki', 'success' => false);
}
$item = self::load_page($arr);
if($item) {
$content = $item['body'];
return [
'content' => $content,
'mimeType' => $w['mimeType'],
'pageMimeType' => $item['mimetype'],
'message' => '',
'success' => true
];
}
return array('content' => null, 'message' => t('Error reading page content'), 'success' => false);
}
static public function page_history($arr) {
$pageUrlName = ((array_key_exists('pageUrlName',$arr)) ? $arr['pageUrlName'] : '');
$resource_id = ((array_key_exists('resource_id',$arr)) ? $arr['resource_id'] : '');
$observer_hash = ((array_key_exists('observer_hash',$arr)) ? $arr['observer_hash'] : '');
$channel_id = ((array_key_exists('channel_id',$arr)) ? $arr['channel_id'] : 0);
$w = Zlib\NativeWiki::get_wiki($channel_id, $observer_hash, $resource_id);
if (!$w['wiki']) {
return array('history' => null, 'message' => 'Error reading wiki', 'success' => false);
}
$items = self::load_page_history($arr);
$history = [];
if($items) {
$processed = 0;
foreach($items as $item) {
if($processed > 1000)
break;
$processed ++;
$history[] = [
'revision' => $item['revision'],
'date' => datetime_convert('UTC',date_default_timezone_get(),$item['edited']),
'name' => $item['author']['xchan_name'],
'title' => get_iconfig($item,'nwikipage','commit_msg')
];
}
return [ 'success' => true, 'history' => $history ];
}
return [ 'success' => false ];
}
static public function load_page($arr) {
$pageUrlName = ((array_key_exists('pageUrlName',$arr)) ? $arr['pageUrlName'] : '');
$resource_id = ((array_key_exists('resource_id',$arr)) ? $arr['resource_id'] : '');
$observer_hash = ((array_key_exists('observer_hash',$arr)) ? $arr['observer_hash'] : '');
$channel_id = ((array_key_exists('channel_id',$arr)) ? $arr['channel_id'] : 0);
$revision = ((array_key_exists('revision',$arr)) ? $arr['revision'] : (-1));
$w = Zlib\NativeWiki::get_wiki($channel_id, $observer_hash, $resource_id);
if (! $w['wiki']) {
return array('content' => null, 'message' => 'Error reading wiki', 'success' => false);
}
$ids = '';
$ic = q("select * from iconfig left join item on iconfig.iid = item.id where uid = %d and cat = 'nwikipage' and k = 'pagetitle' and v = '%s'",
intval($channel_id),
dbesc($pageUrlName)
);
if($ic) {
foreach($ic as $c) {
if($ids)
$ids .= ',';
$ids .= intval($c['iid']);
}
}
$sql_extra = item_permissions_sql($channel_id,$observer_hash);
if($revision == (-1))
$sql_extra .= " order by revision desc ";
elseif($revision)
$sql_extra .= " and revision = " . intval($revision) . " ";
$r = null;
if($ids) {
$r = q("select * from item where resource_type = 'nwikipage' and resource_id = '%s' and uid = %d and id in ( $ids ) $sql_extra limit 1",
dbesc($resource_id),
intval($channel_id)
);
if($r) {
$items = fetch_post_tags($r,true);
return $items[0];
}
}
return null;
}
static public function load_page_history($arr) {
$pageUrlName = ((array_key_exists('pageUrlName',$arr)) ? $arr['pageUrlName'] : '');
$resource_id = ((array_key_exists('resource_id',$arr)) ? $arr['resource_id'] : '');
$observer_hash = ((array_key_exists('observer_hash',$arr)) ? $arr['observer_hash'] : '');
$channel_id = ((array_key_exists('channel_id',$arr)) ? $arr['channel_id'] : 0);
$revision = ((array_key_exists('revision',$arr)) ? $arr['revision'] : (-1));
$w = Zlib\NativeWiki::get_wiki($channel_id, $observer_hash, $resource_id);
if (! $w['wiki']) {
return array('content' => null, 'message' => 'Error reading wiki', 'success' => false);
}
$ids = '';
$ic = q("select * from iconfig left join item on iconfig.iid = item.id where uid = %d and cat = 'nwikipage' and k = 'pagetitle' and v = '%s'",
intval($channel_id),
dbesc($pageUrlName)
);
if($ic) {
foreach($ic as $c) {
if($ids)
$ids .= ',';
$ids .= intval($c['iid']);
}
}
$sql_extra = item_permissions_sql($channel_id,$observer_hash);
$sql_extra .= " order by revision desc ";
$r = null;
if($ids) {
$r = q("select * from item where resource_type = 'nwikipage' and resource_id = '%s' and uid = %d and id in ( $ids ) and item_deleted = 0 $sql_extra",
dbesc($resource_id),
intval($channel_id)
);
if($r) {
xchan_query($r);
$items = fetch_post_tags($r,true);
return $items;
}
}
return null;
}
static public function save_page($arr) {
$pageUrlName = ((array_key_exists('pageUrlName',$arr)) ? $arr['pageUrlName'] : '');
$content = ((array_key_exists('content',$arr)) ? $arr['content'] : '');
$resource_id = ((array_key_exists('resource_id',$arr)) ? $arr['resource_id'] : '');
$observer_hash = ((array_key_exists('observer_hash',$arr)) ? $arr['observer_hash'] : '');
$channel_id = ((array_key_exists('channel_id',$arr)) ? $arr['channel_id'] : 0);
$revision = ((array_key_exists('revision',$arr)) ? $arr['revision'] : 0);
$w = Zlib\NativeWiki::get_wiki($channel_id, $observer_hash, $resource_id);
if (!$w['wiki']) {
return array('message' => t('Error reading wiki'), 'success' => false);
}
// fetch the most recently saved revision.
$item = self::load_page($arr);
if(! $item) {
return array('message' => t('Page not found'), 'success' => false);
}
$mimetype = $item['mimetype'];
// change just the fields we need to change to create a revision;
unset($item['id']);
unset($item['author']);
$item['parent'] = 0;
$item['body'] = $content;
$item['author_xchan'] = $observer_hash;
$item['revision'] = (($arr['revision']) ? intval($arr['revision']) + 1 : intval($item['revision']) + 1);
$item['edited'] = datetime_convert();
$item['mimetype'] = $mimetype;
if($item['iconfig'] && is_array($item['iconfig']) && count($item['iconfig'])) {
for($x = 0; $x < count($item['iconfig']); $x ++) {
unset($item['iconfig'][$x]['id']);
unset($item['iconfig'][$x]['iid']);
}
}
$ret = item_store($item, false, false);
if($ret['item_id'])
return array('message' => '', 'item_id' => $ret['item_id'], 'filename' => $filename, 'success' => true);
else
return array('message' => t('Page update failed.'), 'success' => false);
}
static public function delete_page($arr) {
$pageUrlName = ((array_key_exists('pageUrlName',$arr)) ? $arr['pageUrlName'] : '');
$resource_id = ((array_key_exists('resource_id',$arr)) ? $arr['resource_id'] : '');
$observer_hash = ((array_key_exists('observer_hash',$arr)) ? $arr['observer_hash'] : '');
$channel_id = ((array_key_exists('channel_id',$arr)) ? $arr['channel_id'] : 0);
$w = Zlib\NativeWiki::get_wiki($channel_id, $observer_hash, $resource_id);
if(! $w['wiki']) {
return [ 'success' => false, 'message' => t('Error reading wiki') ];
}
$ids = [];
$ic = q("select * from iconfig left join item on iconfig.iid = item.id
where uid = %d and cat = 'nwikipage' and k = 'pagetitle' and v = '%s'",
intval($channel_id),
dbesc($pageUrlName)
);
if($ic) {
foreach($ic as $c) {
$ids[] = intval($c['iid']);
}
}
if($ids) {
drop_items($ids);
return [ 'success' => true ];
}
return [ 'success' => false, 'message' => t('Nothing deleted') ];
}
static public function revert_page($arr) {
$pageUrlName = ((array_key_exists('pageUrlName',$arr)) ? $arr['pageUrlName'] : '');
$resource_id = ((array_key_exists('resource_id',$arr)) ? $arr['resource_id'] : '');
$commitHash = ((array_key_exists('commitHash',$arr)) ? $arr['commitHash'] : null);
$observer_hash = ((array_key_exists('observer_hash',$arr)) ? $arr['observer_hash'] : '');
$channel_id = ((array_key_exists('channel_id',$arr)) ? $arr['channel_id'] : 0);
if (! $commitHash) {
return array('content' => $content, 'message' => 'No commit was provided', 'success' => false);
}
$w = Zlib\NativeWiki::get_wiki($channel_id, $observer_hash, $resource_id);
if (!$w['wiki']) {
return array('content' => $content, 'message' => 'Error reading wiki', 'success' => false);
}
$x = $arr;
if(intval($commitHash) > 0) {
unset($x['commitHash']);
$x['revision'] = intval($commitHash) - 1;
$loaded = self::load_page($x);
if($loaded) {
$content = $loaded['body'];
return [ 'content' => $content, 'success' => true ];
}
return [ 'content' => $content, 'success' => false ];
}
}
static public function compare_page($arr) {
$pageUrlName = ((array_key_exists('pageUrlName',$arr)) ? $arr['pageUrlName'] : '');
$resource_id = ((array_key_exists('resource_id',$arr)) ? $arr['resource_id'] : '');
$currentCommit = ((array_key_exists('currentCommit',$arr)) ? $arr['currentCommit'] : (-1));
$compareCommit = ((array_key_exists('compareCommit',$arr)) ? $arr['compareCommit'] : 0);
$observer_hash = ((array_key_exists('observer_hash',$arr)) ? $arr['observer_hash'] : '');
$channel_id = ((array_key_exists('channel_id',$arr)) ? $arr['channel_id'] : 0);
$w = Zlib\NativeWiki::get_wiki($channel_id, $observer_hash, $resource_id);
if (!$w['wiki']) {
return array('message' => t('Error reading wiki'), 'success' => false);
}
$x = $arr;
$x['revision'] = (-1);
$currpage = self::load_page($x);
if($currpage)
$currentContent = $currpage['body'];
$x['revision'] = $compareCommit;
$comppage = self::load_page($x);
if($comppage)
$compareContent = $comppage['body'];
if($currpage && $comppage) {
require_once('library/class.Diff.php');
$diff = \Diff::toTable(\Diff::compare($currentContent, $compareContent));
return [ 'success' => true, 'diff' => $diff ];
}
return [ 'success' => false, 'message' => t('Compare: object not found.') ];
}
static public function commit($arr) {
$commit_msg = ((array_key_exists('commit_msg', $arr)) ? $arr['commit_msg'] : t('Page updated'));
$observer_hash = ((array_key_exists('observer_hash',$arr)) ? $arr['observer_hash'] : '');
$channel_id = ((array_key_exists('channel_id',$arr)) ? $arr['channel_id'] : 0);
$pageUrlName = ((array_key_exists('pageUrlName',$arr)) ? $arr['pageUrlName'] : t('Untitled'));
if(array_key_exists('resource_id', $arr)) {
$resource_id = $arr['resource_id'];
}
else {
return array('message' => t('Wiki resource_id required for git commit'), 'success' => false);
}
$w = Zlib\NativeWiki::get_wiki($channel_id, $observer_hash, $resource_id);
if (! $w['wiki']) {
return array('message' => t('Error reading wiki'), 'success' => false);
}
$page = self::load_page($arr);
if($page) {
set_iconfig($page['id'],'nwikipage','commit_msg',escape_tags($commit_msg),true);
return [ 'success' => true, 'item_id' => $page['id'], 'page' => $page ];
}
return [ 'success' => false, 'message' => t('Page not found.') ];
}
static public function convert_links($s, $wikiURL) {
if (strpos($s,'[[') !== false) {
preg_match_all("/\[\[(.*?)\]\]/", $s, $match);
$pages = $pageURLs = array();
foreach ($match[1] as $m) {
// TODO: Why do we need to double urlencode for this to work?
$pageURLs[] = urlencode(urlencode(escape_tags($m)));
$pages[] = $m;
}
$idx = 0;
while(strpos($s,'[[') !== false) {
$replace = '<a href="'.$wikiURL.'/'.$pageURLs[$idx].'">'.$pages[$idx].'</a>';
$s = preg_replace("/\[\[(.*?)\]\]/", $replace, $s, 1);
$idx++;
}
}
return $s;
}
static public function render_page_history($arr) {
$pageUrlName = ((array_key_exists('pageUrlName', $arr)) ? $arr['pageUrlName'] : '');
$resource_id = ((array_key_exists('resource_id', $arr)) ? $arr['resource_id'] : '');
$pageHistory = self::page_history([
'channel_id' => \App::$profile_uid,
'observer_hash' => get_observer_hash(),
'resource_id' => $resource_id,
'pageUrlName' => $pageUrlName
]);
return replace_macros(get_markup_template('nwiki_page_history.tpl'), array(
'$pageHistory' => $pageHistory['history'],
'$permsWrite' => $arr['permsWrite'],
'$name_lbl' => t('Name'),
'$msg_label' => t('Message','wiki_history')
));
}
/**
* Replace the instances of the string [toc] with a list element that will be populated by
* a table of contents by the JavaScript library
* @param string $s
* @return string
*/
static public function generate_toc($s) {
if (strpos($s,'[toc]') !== false) {
//$toc_md = wiki_toc($s); // Generate Markdown-formatted list prior to HTML render
$toc_md = '<ul id="wiki-toc"></ul>'; // use the available jQuery plugin http://ndabas.github.io/toc/
$s = preg_replace("/\[toc\]/", $toc_md, $s, -1);
}
return $s;
}
/**
* Converts a select set of bbcode tags. Much of the code is copied from include/bbcode.php
* @param string $s
* @return string
*/
static public function bbcode($s) {
$s = str_replace(array('[baseurl]', '[sitename]'), array(z_root(), get_config('system', 'sitename')), $s);
$s = preg_replace_callback("/\[observer\.language\=(.*?)\](.*?)\[\/observer\]/ism",'oblanguage_callback', $s);
$s = preg_replace_callback("/\[observer\.language\!\=(.*?)\](.*?)\[\/observer\]/ism",'oblanguage_necallback', $s);
$observer = \App::get_observer();
if ($observer) {
$s1 = '<span class="bb_observer" title="' . t('Different viewers will see this text differently') . '">';
$s2 = '</span>';
$obsBaseURL = $observer['xchan_connurl'];
$obsBaseURL = preg_replace("/\/poco\/.*$/", '', $obsBaseURL);
$s = str_replace('[observer.baseurl]', $obsBaseURL, $s);
$s = str_replace('[observer.url]', $observer['xchan_url'], $s);
$s = str_replace('[observer.name]', $s1 . $observer['xchan_name'] . $s2, $s);
$s = str_replace('[observer.address]', $s1 . $observer['xchan_addr'] . $s2, $s);
$s = str_replace('[observer.webname]', substr($observer['xchan_addr'], 0, strpos($observer['xchan_addr'], '@')), $s);
$s = str_replace('[observer.photo]', '', $s);
}
else {
$s = str_replace('[observer.baseurl]', '', $s);
$s = str_replace('[observer.url]', '', $s);
$s = str_replace('[observer.name]', '', $s);
$s = str_replace('[observer.address]', '', $s);
$s = str_replace('[observer.webname]', '', $s);
$s = str_replace('[observer.photo]', '', $s);
}
return $s;
}
static public function get_file_ext($arr) {
if($arr['mimetype'] === 'text/bbcode')
return '.bb';
elseif($arr['mimetype'] === 'text/markdown')
return '.md';
elseif($arr['mimetype'] === 'text/plain')
return '.txt';
}
// This function is derived from
// http://stackoverflow.com/questions/32068537/generate-table-of-contents-from-markdown-in-php
static public function toc($content) {
// ensure using only "\n" as line-break
$source = str_replace(["\r\n", "\r"], "\n", $content);
// look for markdown TOC items
preg_match_all(
'/^(?:=|-|#).*$/m',
$source,
$matches,
PREG_PATTERN_ORDER | PREG_OFFSET_CAPTURE
);
// preprocess: iterate matched lines to create an array of items
// where each item is an array(level, text)
$file_size = strlen($source);
foreach ($matches[0] as $item) {
$found_mark = substr($item[0], 0, 1);
if ($found_mark == '#') {
// text is the found item
$item_text = $item[0];
$item_level = strrpos($item_text, '#') + 1;
$item_text = substr($item_text, $item_level);
} else {
// text is the previous line (empty if <hr>)
$item_offset = $item[1];
$prev_line_offset = strrpos($source, "\n", -($file_size - $item_offset + 2));
$item_text =
substr($source, $prev_line_offset, $item_offset - $prev_line_offset - 1);
$item_text = trim($item_text);
$item_level = $found_mark == '=' ? 1 : 2;
}
if (!trim($item_text) OR strpos($item_text, '|') !== FALSE) {
// item is an horizontal separator or a table header, don't mind
continue;
}
$raw_toc[] = ['level' => $item_level, 'text' => trim($item_text)];
}
$o = '';
foreach($raw_toc as $t) {
$level = intval($t['level']);
$text = $t['text'];
switch ($level) {
case 1:
$li = '* ';
break;
case 2:
$li = ' * ';
break;
case 3:
$li = ' * ';
break;
case 4:
$li = ' * ';
break;
default:
$li = '* ';
break;
}
$o .= $li . $text . "\n";
}
return $o;
}
}

View File

@@ -1,21 +1,8 @@
<?php
<?php /** @file */
namespace Zotlabs\Lib;
/**
* @brief Class for handling channel specific configurations.
*
* <b>PConfig</b> is used for channel specific configurations and takes a
* <i>channel_id</i> as identifier. It stores for example which features are
* enabled per channel. The storage is of size MEDIUMTEXT.
*
* @code{.php}$var = Zotlabs\Lib\PConfig::Get('uid', 'category', 'key');
* // with default value for non existent key
* $var = Zotlabs\Lib\PConfig::Get('uid', 'category', 'unsetkey', 'defaultvalue');@endcode
*
* The old (deprecated?) way to access a PConfig value is:
* @code{.php}$var = get_pconfig(local_channel(), 'category', 'key');@endcode
*/
class PConfig {
/**
@@ -26,18 +13,18 @@ class PConfig {
*
* @param string $uid
* The channel_id
* @return void|false Nothing or false if $uid is null or false
* @return void|false Nothing or false if $uid is false
*/
static public function Load($uid) {
if(is_null($uid) || $uid === false)
return false;
if(! is_array(\App::$config)) {
btlogger('App::$config not an array');
}
if(! array_key_exists($uid, \App::$config)) {
if(! array_key_exists($uid, \App::$config))
\App::$config[$uid] = array();
if(! is_array(\App::$config)) {
btlogger('App::$config not an array: ' . $uid);
}
if(! is_array(\App::$config[$uid])) {
@@ -76,25 +63,26 @@ class PConfig {
* The category of the configuration value
* @param string $key
* The configuration key to query
* @param mixed $default (optional, default false)
* Default value to return if key does not exist
* @param boolean $instore (deprecated, without function)
* @return mixed Stored value or false if it does not exist
*/
static public function Get($uid, $family, $key, $default = false) {
static public function Get($uid,$family,$key,$instore = false) {
if(is_null($uid) || $uid === false)
return $default;
return false;
if(! array_key_exists($uid, \App::$config))
self::Load($uid);
if((! array_key_exists($family, \App::$config[$uid])) || (! array_key_exists($key, \App::$config[$uid][$family])))
return $default;
return false;
return ((! is_array(\App::$config[$uid][$family][$key])) && (preg_match('|^a:[0-9]+:{.*}$|s', \App::$config[$uid][$family][$key]))
return ((! is_array(\App::$config[$uid][$family][$key])) && (preg_match('|^a:[0-9]+:{.*}$|s', \App::$config[$uid][$family][$key]))
? unserialize(\App::$config[$uid][$family][$key])
: \App::$config[$uid][$family][$key]
);
}
/**
@@ -113,11 +101,12 @@ class PConfig {
* The value to store
* @return mixed Stored $value or false
*/
static public function Set($uid, $family, $key, $value) {
// this catches subtle errors where this function has been called
// this catches subtle errors where this function has been called
// with local_channel() when not logged in (which returns false)
// and throws an error in array_key_exists below.
// and throws an error in array_key_exists below.
// we provide a function backtrace in the logs so that we can find
// and fix the calling function.
@@ -130,7 +119,7 @@ class PConfig {
$dbvalue = ((is_array($value)) ? serialize($value) : $value);
$dbvalue = ((is_bool($dbvalue)) ? intval($dbvalue) : $dbvalue);
if(self::Get($uid, $family, $key) === false) {
if(get_pconfig($uid, $family, $key) === false) {
if(! array_key_exists($uid, \App::$config))
\App::$config[$uid] = array();
if(! array_key_exists($family, \App::$config[$uid]))
@@ -142,6 +131,7 @@ class PConfig {
dbesc($key),
dbesc($dbvalue)
);
}
else {
@@ -151,6 +141,7 @@ class PConfig {
dbesc($family),
dbesc($key)
);
}
// keep a separate copy for all variables which were
@@ -186,6 +177,7 @@ class PConfig {
* The configuration key to delete
* @return mixed
*/
static public function Delete($uid, $family, $key) {
if(is_null($uid) || $uid === false)
@@ -193,19 +185,16 @@ class PConfig {
$ret = false;
if(array_key_exists($uid,\App::$config)
&& is_array(\App::$config['uid'])
&& array_key_exists($family,\App::$config['uid'])
&& array_key_exists($key, \App::$config[$uid][$family]))
if(array_key_exists($key, \App::$config[$uid][$family]))
unset(\App::$config[$uid][$family][$key]);
$ret = q("DELETE FROM pconfig WHERE uid = %d AND cat = '%s' AND k = '%s'",
intval($uid),
dbesc($family),
dbesc($key)
);
$ret = q("DELETE FROM pconfig WHERE uid = %d AND cat = '%s' AND k = '%s'",
intval($uid),
dbesc($family),
dbesc($key)
);
return $ret;
}
}

View File

@@ -1,186 +0,0 @@
<?php
namespace Zotlabs\Lib;
use Zotlabs\Access\PermissionRoles;
use Zotlabs\Access\Permissions;
/**
* @brief Permission Categories. Permission rules for various classes of connections.
*
* Connection permissions answer the question "Can Joe view my photos?"
*
* Some permissions may be inherited from the channel's "privacy settings"
* (@ref ::Zotlabs::Access::PermissionLimits "PermissionLimits") "Who can view my
* photos (at all)?" which have higher priority than individual connection settings.
* We evaluate permission limits first, and then fall through to connection
* permissions if the permission limits didn't already make a definitive decision.
*
* After PermissionLimits and connection permissions are evaluated, individual
* content ACLs are evaluated (@ref ::Zotlabs::Access::AccessList "AccessList").
* These answer the question "Can Joe view *this* album/photo?".
*/
class Permcat {
/**
* @var array
*/
private $permcats = [];
/**
* @brief Permcat constructor.
*
* @param int $channel_id
*/
public function __construct($channel_id) {
$perms = [];
// first check role perms for a perms_connect setting
$role = get_pconfig($channel_id,'system','permissions_role');
if($role) {
$x = PermissionRoles::role_perms($role);
if($x['perms_connect']) {
$perms = Permissions::FilledPerms($x['perms_connect']);
}
}
// if no role perms it may be a custom role, see if there any autoperms
if(! $perms) {
$perms = Permissions::FilledAutoPerms($channel_id);
}
// if no autoperms it may be a custom role with manual perms
if(! $perms) {
$r = q("select channel_hash from channel where channel_id = %d",
intval($channel_id)
);
if($r) {
$x = q("select * from abconfig where chan = %d and xchan = '%s' and cat = 'my_perms'",
intval($channel_id),
dbesc($r[0]['channel_hash'])
);
if($x) {
foreach($x as $xv) {
$perms[$xv['k']] = intval($xv['v']);
}
}
}
}
// nothing was found - create a filled permission array where all permissions are 0
if(! $perms) {
$perms = Permissions::FilledPerms([]);
}
$this->permcats[] = [
'name' => 'default',
'localname' => t('default','permcat'),
'perms' => Permissions::Operms($perms),
'system' => 1
];
$p = $this->load_permcats($channel_id);
if($p) {
for($x = 0; $x < count($p); $x++) {
$this->permcats[] = [
'name' => $p[$x][0],
'localname' => $p[$x][1],
'perms' => Permissions::Operms(Permissions::FilledPerms($p[$x][2])),
'system' => intval($p[$x][3])
];
}
}
}
/**
* @brief Return array with permcats.
*
* @return array
*/
public function listing() {
return $this->permcats;
}
/**
* @brief
*
* @param string $name
* @return array
* * \e array with permcats
* * \e bool \b error if $name not found in permcats true
*/
public function fetch($name) {
if($name && $this->permcats) {
foreach($this->permcats as $permcat) {
if(strcasecmp($permcat['name'], $name) === 0) {
return $permcat;
}
}
}
return ['error' => true];
}
public function load_permcats($uid) {
$permcats = [
[ 'follower', t('follower','permcat'),
[ 'view_stream','view_profile','view_contacts','view_storage','view_pages','view_wiki',
'post_like' ], 1
],
[ 'contributor', t('contributor','permcat'),
[ 'view_stream','view_profile','view_contacts','view_storage','view_pages','view_wiki',
'post_wall','post_comments','write_wiki','post_like','tag_deliver','chat' ], 1
],
[ 'publisher', t('publisher','permcat'),
[ 'view_stream','view_profile','view_contacts','view_storage','view_pages',
'write_storage','post_wall','write_pages','write_wiki','post_comments','post_like','tag_deliver',
'chat', 'republish' ], 1
]
];
if($uid) {
$x = q("select * from pconfig where uid = %d and cat = 'permcat'",
intval($uid)
);
if($x) {
foreach($x as $xv) {
$value = ((preg_match('|^a:[0-9]+:{.*}$|s', $xv['v'])) ? unserialize($xv['v']) : $xv['v']);
$permcats[] = [ $xv['k'], $xv['k'], $value, 0 ];
}
}
}
/**
* @hooks permcats
* * \e array
*/
call_hooks('permcats', $permcats);
return $permcats;
}
static public function find_permcat($arr, $name) {
if((! $arr) || (! $name))
return false;
foreach($arr as $p)
if($p['name'] == $name)
return $p['value'];
}
static public function update($channel_id, $name, $permarr) {
PConfig::Set($channel_id, 'permcat', $name, $permarr);
}
static public function delete($channel_id, $name) {
PConfig::Delete($channel_id, 'permcat', $name);
}
}

View File

@@ -12,25 +12,22 @@ require_once("include/text.php");
* permission settings for an item with an empty ACL.
* i.e the caption, icon, and tooltip for the no-ACL option in the ACL dialog.
*/
class PermissionDescription {
class PermissionDescription {
private $global_perm;
private $channel_perm;
private $fallback_description;
/**
* Constructor is private.
* Use static methods fromGlobalPermission(), fromStandalonePermission(),
* or fromDescription() to create instances.
*
* @internal
* @param int $global_perm
* @param int $channel_perm
* @param string $description (optional) default empty
* Use static methods fromGlobalPermission(), fromStandalonePermission(), or fromDescription()
* to create instances.
*/
private function __construct($global_perm, $channel_perm, $description = '') {
$this->global_perm = $global_perm;
$this->channel_perm = $channel_perm;
$this->fallback_description = ($description == '') ? t('Visible to your default audience') : $description;
}
@@ -46,22 +43,23 @@ class PermissionDescription {
return new PermissionDescription('', 0x80000, $description);
}
/**
* Use this method only if the interpretation of an empty ACL doesn't fall back to a global
* default permission. You should pass one of the constants from boot.php - PERMS_PUBLIC,
* PERMS_NETWORK etc.
*
*
* @param integer $perm - a single enumerated constant permission - PERMS_PUBLIC, PERMS_NETWORK etc.
* @return a new instance of PermissionDescription
*/
public static function fromStandalonePermission($perm) {
$result = new PermissionDescription('', $perm);
$checkPerm = $result->get_permission_description();
if($checkPerm == $result->fallback_description) {
$checkPerm = $this->get_permission_description();
if ($checkPerm == $this->fallback_description) {
$result = null;
logger('null PermissionDescription from unknown standalone permission: ' . $perm, LOGGER_DEBUG, LOG_ERR);
logger('null PermissionDescription from unknown standalone permission: ' . $perm ,LOGGER_DEBUG, LOG_ERROR);
}
return $result;
@@ -69,9 +67,9 @@ class PermissionDescription {
/**
* This is the preferred way to create a PermissionDescription, as it provides the most details.
* Use this method if you know an empty ACL will result in one of the global default permissions
* Use this method if you know an empty ACL will result in one of the global default permissions
* being used, such as channel_r_stream (for which you would pass 'view_stream').
*
*
* @param string $permname - a key for the global perms array from get_perms() in permissions.php,
* e.g. 'view_stream', 'view_profile', etc.
* @return a new instance of PermissionDescription
@@ -82,19 +80,19 @@ class PermissionDescription {
$global_perms = \Zotlabs\Access\Permissions::Perms();
if(array_key_exists($permname, $global_perms)) {
if (array_key_exists($permname, $global_perms)) {
$channelPerm = \Zotlabs\Access\PermissionLimits::Get(\App::$channel['channel_id'], $permname);
$channelPerm = \Zotlabs\Access\PermissionLimits::Get(\App::$channel['channel_id'],$permname);
$result = new PermissionDescription('', $channelPerm);
} else {
// The acl dialog can handle null arguments, but it shouldn't happen
logger('null PermissionDescription from unknown global permission: ' . $permname, LOGGER_DEBUG, LOG_ERR);
logger('null PermissionDescription from unknown global permission: ' . $permname ,LOGGER_DEBUG, LOG_ERROR);
}
return $result;
}
/**
* Gets a localized description of the permission, or a generic message if the permission
* is unknown.
@@ -103,7 +101,8 @@ class PermissionDescription {
*/
public function get_permission_description() {
switch($this->channel_perm) {
switch($this->channel_perm) {
case 0: return t('Only me');
case PERMS_PUBLIC: return t('Public');
case PERMS_NETWORK: return t('Anybody in the $Projectname network');
@@ -118,18 +117,19 @@ class PermissionDescription {
/**
* Returns an icon css class name if an appropriate one is available, e.g. "fa-globe" for Public,
* otherwise returns empty string.
* otherwise returns empty string.
*
* @return string icon css class name (often FontAwesome)
*/
public function get_permission_icon() {
switch($this->channel_perm) {
switch($this->channel_perm) {
case 0:/* only me */ return 'fa-eye-slash';
case PERMS_PUBLIC: return 'fa-globe';
case PERMS_NETWORK: return 'fa-share-alt-square'; // fa-share-alt-square is very similiar to the hubzilla logo, but we should create our own logo class to use
case PERMS_SITE: return 'fa-sitemap';
case PERMS_CONTACTS: return 'fa-group';
case PERMS_SITE: return 'fa-sitemap';
case PERMS_CONTACTS: return 'fa-group';
case PERMS_SPECIFIC: return 'fa-list';
case PERMS_AUTHED: return '';
case PERMS_PENDING: return '';
@@ -137,6 +137,7 @@ class PermissionDescription {
}
}
/**
* Returns a localized description of where the permission came from, if this is known.
* If it's not know, or if the permission is standalone and didn't come from a default
@@ -146,7 +147,8 @@ class PermissionDescription {
*/
public function get_permission_origin_description() {
switch($this->global_perm) {
switch($this->global_perm) {
case PERMS_R_STREAM: return t('This is your default setting for the audience of your normal stream, and posts.');
case PERMS_R_PROFILE: return t('This is your default setting for who can view your default channel profile');
case PERMS_R_ABOOK: return t('This is your default setting for who can view your connections');

View File

@@ -0,0 +1,19 @@
<?php /** @file */
namespace Zotlabs\Lib;
/*
* Abstraction class for dealing with alternate networks (which of course do not exist, hence the abstraction)
*/
abstract class ProtoDriver {
abstract protected function discover($channel,$location);
abstract protected function deliver($item,$channel,$recipients);
abstract protected function collect($channel,$connection);
abstract protected function change_permissions($permissions,$channel,$recipient);
abstract protected function acknowledge_permissions($permissions,$channel,$recipient);
abstract protected function deliver_private($item,$channel,$recipients);
abstract protected function collect_private($channel,$connection);
}

View File

@@ -1,28 +0,0 @@
<?php
namespace Zotlabs\Lib;
/**
* @brief Account configuration storage is built on top of the under-utilised xconfig.
*
* @see XConfig
*/
class SConfig {
static public function Load($server_id) {
return XConfig::Load('s_' . $server_id);
}
static public function Get($server_id,$family,$key,$default = false) {
return XConfig::Get('s_' . $server_id,$family,$key, $default);
}
static public function Set($server_id,$family,$key,$value) {
return XConfig::Set('s_' . $server_id,$family,$key,$value);
}
static public function Delete($server_id,$family,$key) {
return XConfig::Delete('s_' . $server_id,$family,$key);
}
}

View File

@@ -1,143 +0,0 @@
<?php
namespace Zotlabs\Lib;
class Share {
private $item = null;
public function __construct($post_id) {
if(! $post_id)
return;
if(! (local_channel() || remote_channel()))
return;
$r = q("SELECT * from item left join xchan on author_xchan = xchan_hash WHERE id = %d LIMIT 1",
intval($post_id)
);
if(! $r)
return;
if(($r[0]['item_private']) && ($r[0]['xchan_network'] !== 'rss'))
return;
$sql_extra = item_permissions_sql($r[0]['uid']);
$r = q("select * from item where id = %d $sql_extra",
intval($post_id)
);
if(! $r)
return;
if($r[0]['mimetype'] !== 'text/bbcode')
return;
/** @FIXME eventually we want to post remotely via rpost on your home site */
// When that works remove this next bit:
if(! local_channel())
return;
xchan_query($r);
$this->item = $r[0];
return;
}
public function obj() {
$obj = [];
if(! $this->item)
return $obj;
$obj['type'] = $this->item['obj_type'];
$obj['id'] = $this->item['mid'];
$obj['content'] = $this->item['body'];
$obj['content_type'] = $this->item['mimetype'];
$obj['title'] = $this->item['title'];
$obj['created'] = $this->item['created'];
$obj['edited'] = $this->item['edited'];
$obj['author'] = [
'name' => $this->item['author']['xchan_name'],
'address' => $this->item['author']['xchan_addr'],
'network' => $this->item['author']['xchan_network'],
'link' => [
[
'rel' => 'alternate',
'type' => 'text/html',
'href' => $this->item['author']['xchan_url']
],
[
'rel' => 'photo',
'type' => $this->item['author']['xchan_photo_mimetype'],
'href' => $this->item['author']['xchan_photo_m']
]
]
];
$obj['owner'] = [
'name' => $this->item['owner']['xchan_name'],
'address' => $this->item['owner']['xchan_addr'],
'network' => $this->item['owner']['xchan_network'],
'link' => [
[
'rel' => 'alternate',
'type' => 'text/html',
'href' => $this->item['owner']['xchan_url']
],
[
'rel' => 'photo',
'type' => $this->item['owner']['xchan_photo_mimetype'],
'href' => $this->item['owner']['xchan_photo_m']
]
]
];
$obj['link'] = [
'rel' => 'alternate',
'type' => 'text/html',
'href' => $this->item['plink']
];
return $obj;
}
public function bbcode() {
$bb = EMPTY_STR;
if(! $this->item)
return $bb;
$is_photo = (($this->item['obj_type'] === ACTIVITY_OBJ_PHOTO) ? true : false);
if($is_photo) {
$object = json_decode($this->item['obj'],true);
$photo_bb = $object['body'];
}
if (strpos($this->item['body'], "[/share]") !== false) {
$pos = strpos($this->item['body'], "[share");
$bb = substr($this->item['body'], $pos);
} else {
$bb = "[share author='".urlencode($this->item['author']['xchan_name']).
"' profile='" . $this->item['author']['xchan_url'] .
"' avatar='" . $this->item['author']['xchan_photo_s'] .
"' link='" . $this->item['plink'] .
"' auth='" . (($this->item['author']['network'] === 'zot') ? 'true' : 'false') .
"' posted='" . $this->item['created'] .
"' message_id='" . $this->item['mid'] .
"']";
if($this->item['title'])
$bb .= '[b]'.$this->item['title'].'[/b]'."\r\n";
$bb .= (($is_photo) ? $photo_bb . "\r\n" . $this->item['body'] : $this->item['body']);
$bb .= "[/share]";
}
return $bb;
}
}

View File

@@ -19,9 +19,6 @@ class System {
static public function get_project_version() {
if(is_array(\App::$config) && is_array(\App::$config['system']) && \App::$config['system']['hide_version'])
return '';
if(is_array(\App::$config) && is_array(\App::$config['system']) && array_key_exists('std_version',\App::$config['system']))
return \App::$config['system']['std_version'];
return self::get_std_version();
}
@@ -35,37 +32,20 @@ class System {
static public function get_notify_icon() {
if(is_array(\App::$config) && is_array(\App::$config['system']) && \App::$config['system']['email_notify_icon_url'])
return \App::$config['system']['email_notify_icon_url'];
return z_root() . DEFAULT_NOTIFY_ICON;
return z_root() . '/images/hz-white-32.png';
}
static public function get_site_icon() {
if(is_array(\App::$config) && is_array(\App::$config['system']) && \App::$config['system']['site_icon_url'])
return \App::$config['system']['site_icon_url'];
return z_root() . DEFAULT_PLATFORM_ICON ;
return z_root() . '/images/hz-32.png';
}
static public function get_project_link() {
if(is_array(\App::$config) && is_array(\App::$config['system']) && \App::$config['system']['project_link'])
return \App::$config['system']['project_link'];
return 'https://hubzilla.org';
}
static public function get_project_srclink() {
if(is_array(\App::$config) && is_array(\App::$config['system']) && \App::$config['system']['project_srclink'])
return \App::$config['system']['project_srclink'];
return 'https://framagit.org/hubzilla/core.git';
}
static public function get_server_role() {
return 'pro';
}
static public function get_zot_revision() {
$x = [ 'revision' => ZOT_REVISION ];
call_hooks('zot_revision',$x);
return $x['revision'];
if(is_array(\App::$config) && is_array(\App::$config['system']) && \App::$config['system']['server_role'])
return \App::$config['system']['server_role'];
return 'standard';
}
static public function get_std_version() {
@@ -74,12 +54,5 @@ class System {
return '0.0.0';
}
static public function compatible_project($p) {
if(get_directory_realm() != DIRECTORY_REALM)
return true;
if(in_array(strtolower($p),['hubzilla','zap','red']))
return true;
return false;
}
}

View File

@@ -1,21 +0,0 @@
<?php
namespace Zotlabs\Lib;
class Techlevels {
static public function levels() {
$techlevels = [
'0' => t('0. Beginner/Basic'),
'1' => t('1. Novice - not skilled but willing to learn'),
'2' => t('2. Intermediate - somewhat comfortable'),
'3' => t('3. Advanced - very comfortable'),
'4' => t('4. Expert - I can write computer code'),
'5' => t('5. Wizard - I probably know more than you do')
];
return $techlevels;
}
}

View File

@@ -29,7 +29,6 @@ class ThreadItem {
private $visiting = false;
private $channel = null;
private $display_mode = 'normal';
private $reload = '';
public function __construct($data) {
@@ -38,14 +37,14 @@ class ThreadItem {
$this->toplevel = ($this->get_id() == $this->get_data_value('parent'));
// Prepare the children
if($data['children']) {
if(count($data['children'])) {
foreach($data['children'] as $item) {
/*
* Only add those that will be displayed
*/
if((! visible_activity($item)) || array_key_exists('blocked',$item)) {
if((! visible_activity($item)) || array_key_exists('author_blocked',$item)) {
continue;
}
@@ -83,8 +82,7 @@ class ThreadItem {
$dropping = false;
$star = false;
$isstarred = "unstarred fa-star-o";
$is_comment = false;
$is_item = false;
$indent = '';
$osparkle = '';
$total_children = $this->count_descendants();
$unseen_comments = (($item['real_uid']) ? 0 : $this->count_unseen_descendants());
@@ -102,30 +100,10 @@ class ThreadItem {
if($item['author']['xchan_network'] === 'rss')
$shareable = true;
$privacy_warning = false;
if(($item['item_private'] == 1) && ($item['owner']['xchan_network'] === 'activitypub')) {
$recips = get_iconfig($item['parent'], 'activitypub', 'recips');
if(! in_array($observer['xchan_url'], $recips['to']))
$privacy_warning = true;
}
$mode = $conv->get_mode();
switch($item['item_type']) {
case ITEM_TYPE_CARD:
$edlink = 'card_edit';
break;
case ITEM_TYPE_ARTICLE:
$edlink = 'article_edit';
break;
default:
$edlink = 'editpost';
break;
}
if(local_channel() && $observer['xchan_hash'] === $item['author_xchan'])
$edpost = array(z_root() . '/' . $edlink . '/' . $item['id'], t('Edit'));
$edpost = array(z_root()."/editpost/".$item['id'], t("Edit"));
else
$edpost = false;
@@ -148,10 +126,6 @@ class ThreadItem {
'delete' => t('Delete'),
);
}
elseif(is_site_admin()) {
$drop = [ 'dropping' => true, 'delete' => t('Admin Delete') ];
}
// FIXME
if($observer_is_pageowner) {
$multidrop = array(
@@ -162,7 +136,7 @@ class ThreadItem {
$filer = ((($conv->get_profile_owner() == local_channel()) && (! array_key_exists('real_uid',$item))) ? t("Save to Folder") : false);
$profile_avatar = $item['author']['xchan_photo_m'];
$profile_link = chanlink_hash($item['author_xchan']);
$profile_link = chanlink_url($item['author']['xchan_url']);
$profile_name = $item['author']['xchan_name'];
$location = format_location($item);
@@ -178,7 +152,7 @@ class ThreadItem {
$response_verbs[] = 'attendyes';
$response_verbs[] = 'attendno';
$response_verbs[] = 'attendmaybe';
if($this->is_commentable() && $observer) {
if($this->is_commentable()) {
$isevent = true;
$attend = array( t('I will attend'), t('I will not attend'), t('I might attend'));
}
@@ -189,7 +163,7 @@ class ThreadItem {
$response_verbs[] = 'agree';
$response_verbs[] = 'disagree';
$response_verbs[] = 'abstain';
if($this->is_commentable() && $observer) {
if($this->is_commentable()) {
$conlabels = array( t('I agree'), t('I disagree'), t('I abstain'));
$canvote = true;
}
@@ -207,9 +181,9 @@ class ThreadItem {
$like_count = ((x($conv_responses['like'],$item['mid'])) ? $conv_responses['like'][$item['mid']] : '');
$like_list = ((x($conv_responses['like'],$item['mid'])) ? $conv_responses['like'][$item['mid'] . '-l'] : '');
if (($like_list) && (count($like_list) > MAX_LIKERS)) {
if (count($like_list) > MAX_LIKERS) {
$like_list_part = array_slice($like_list, 0, MAX_LIKERS);
array_push($like_list_part, '<a class="dropdown-item" href="#" data-toggle="modal" data-target="#likeModal-' . $this->get_id() . '"><b>' . t('View all') . '</b></a>');
array_push($like_list_part, '<a href="#" data-toggle="modal" data-target="#likeModal-' . $this->get_id() . '"><b>' . t('View all') . '</b></a>');
} else {
$like_list_part = '';
}
@@ -219,9 +193,9 @@ class ThreadItem {
$dislike_count = ((x($conv_responses['dislike'],$item['mid'])) ? $conv_responses['dislike'][$item['mid']] : '');
$dislike_list = ((x($conv_responses['dislike'],$item['mid'])) ? $conv_responses['dislike'][$item['mid'] . '-l'] : '');
$dislike_button_label = tt('Dislike','Dislikes',$dislike_count,'noun');
if (($dislike_list) && (count($dislike_list) > MAX_LIKERS)) {
if (count($dislike_list) > MAX_LIKERS) {
$dislike_list_part = array_slice($dislike_list, 0, MAX_LIKERS);
array_push($dislike_list_part, '<a class="dropdown-item" href="#" data-toggle="modal" data-target="#dislikeModal-' . $this->get_id() . '"><b>' . t('View all') . '</b></a>');
array_push($dislike_list_part, '<a href="#" data-toggle="modal" data-target="#dislikeModal-' . $this->get_id() . '"><b>' . t('View all') . '</b></a>');
} else {
$dislike_list_part = '';
}
@@ -243,15 +217,22 @@ class ThreadItem {
// FIXME check this permission
if(($conv->get_profile_owner() == local_channel()) && (! array_key_exists('real_uid',$item))) {
// FIXME we don't need all this stuff, some can be done in the template
$star = array(
'do' => t("Add Star"),
'undo' => t("Remove Star"),
'toggle' => t("Toggle Star Status"),
'isstarred' => ((intval($item['item_starred'])) ? true : false),
'classdo' => (intval($item['item_starred']) ? "hidden" : ""),
'classundo' => (intval($item['item_starred']) ? "" : "hidden"),
'isstarred' => (intval($item['item_starred']) ? "starred fa-star" : "unstarred fa-star-o"),
'starred' => t('starred'),
);
}
}
else {
$is_comment = true;
$indent = 'comment';
}
@@ -269,6 +250,8 @@ class ThreadItem {
);
}
$server_role = get_config('system','server_role');
$has_bookmarks = false;
if(is_array($item['term'])) {
foreach($item['term'] as $t) {
@@ -281,7 +264,7 @@ class ThreadItem {
if(($item['obj_type'] === ACTIVITY_OBJ_EVENT) && $conv->get_profile_owner() == local_channel())
$has_event = true;
if($this->is_commentable() && $observer) {
if($this->is_commentable()) {
$like = array( t("I like this \x28toggle\x29"), t("like"));
$dislike = array( t("I don't like this \x28toggle\x29"), t("dislike"));
}
@@ -293,13 +276,13 @@ class ThreadItem {
$keep_reports = intval(get_config('system','expire_delivery_reports'));
if($keep_reports === 0)
$keep_reports = 10;
$keep_reports = 30;
if((! get_config('system','disable_dreport')) && strcmp(datetime_convert('UTC','UTC',$item['created']),datetime_convert('UTC','UTC',"now - $keep_reports days")) > 0)
$dreport = t('Delivery Report');
if(strcmp(datetime_convert('UTC','UTC',$item['created']),datetime_convert('UTC','UTC','now - 12 hours')) > 0)
$is_new = true;
$indent .= ' shiny';
localize_item($item);
@@ -312,12 +295,12 @@ class ThreadItem {
$owner_address = substr($item['owner']['xchan_addr'],0,strpos($item['owner']['xchan_addr'],'@'));
$viewthread = $item['llink'];
if($conv->get_mode() === 'channel')
$viewthread = z_root() . '/channel/' . $owner_address . '?f=&mid=' . urlencode($item['mid']);
$viewthread = z_root() . '/channel/' . $owner_address . '?f=&mid=' . $item['mid'];
$comment_count_txt = sprintf( tt('%d comment','%d comments',$total_children),$total_children );
$list_unseen_txt = (($unseen_comments) ? sprintf('%d unseen',$unseen_comments) : '');
@@ -327,8 +310,7 @@ class ThreadItem {
$tmp_item = array(
'template' => $this->get_template(),
'mode' => $mode,
'item_type' => intval($item['item_type']),
'mode' => $mode,
'type' => implode("",array_slice(explode("/",$item['verb']),-1)),
'body' => $body['html'],
'tags' => $body['tags'],
@@ -353,8 +335,7 @@ class ThreadItem {
'wall' => t('Wall-to-Wall'),
'vwall' => t('via Wall-To-Wall:'),
'profile_url' => $profile_link,
'thread_action_menu' => thread_action_menu($item,$conv->get_mode()),
'thread_author_menu' => thread_author_menu($item,$conv->get_mode()),
'item_photo_menu' => item_photo_menu($item),
'dreport' => $dreport,
'name' => $profile_name,
'thumb' => $profile_avatar,
@@ -370,18 +351,11 @@ class ThreadItem {
'editedtime' => (($item['edited'] != $item['created']) ? sprintf( t('last edited: %s'), datetime_convert('UTC', date_default_timezone_get(), $item['edited'], 'r')) : ''),
'expiretime' => (($item['expires'] > NULL_DATE) ? sprintf( t('Expires: %s'), datetime_convert('UTC', date_default_timezone_get(), $item['expires'], 'r')):''),
'lock' => $lock,
'privacy_warning' => $privacy_warning,
'verified' => $verified,
'unverified' => $unverified,
'forged' => $forged,
'location' => $location,
'divider' => get_pconfig($conv->get_profile_owner(),'system','item_divider'),
'attend_label' => t('Attend'),
'attend_title' => t('Attendance Options'),
'vote_label' => t('Vote'),
'vote_title' => t('Voting Options'),
'is_comment' => $is_comment,
'is_new' => $is_new,
'indent' => $indent,
'owner_url' => $this->get_owner_url(),
'owner_photo' => $this->get_owner_photo(),
'owner_name' => $this->get_owner_name(),
@@ -390,7 +364,7 @@ class ThreadItem {
'has_tags' => $has_tags,
'reactions' => $this->reactions,
// Item toolbar buttons
'emojis' => (($this->is_toplevel() && $this->is_commentable() && $observer && feature_enabled($conv->get_profile_owner(),'emojis')) ? '1' : ''),
'emojis' => (($this->is_toplevel() && $this->is_commentable() && feature_enabled($conv->get_profile_owner(),'emojis')) ? '1' : ''),
'like' => $like,
'dislike' => ((feature_enabled($conv->get_profile_owner(),'dislike')) ? $dislike : ''),
'share' => $share,
@@ -427,10 +401,9 @@ class ThreadItem {
'showlike' => $showlike,
'showdislike' => $showdislike,
'comment' => $this->get_comment_box($indent),
'previewing' => ($conv->is_preview() ? true : false ),
'preview_lbl' => t('This is an unsaved preview'),
'previewing' => ($conv->is_preview() ? ' preview ' : ''),
'wait' => t('Please wait'),
'submid' => str_replace(['+','='], ['',''], base64_encode($item['mid'])),
'submid' => substr($item['mid'],0,32),
'thread_level' => $thread_level
);
@@ -501,14 +474,6 @@ class ThreadItem {
return $this->threaded;
}
public function set_reload($val) {
$this->reload = $val;
}
public function get_reload() {
return $this->reload;
}
public function set_commentable($val) {
$this->commentable = $val;
foreach($this->get_children() as $child)
@@ -735,19 +700,22 @@ class ThreadItem {
$observer = $conv->get_observer();
$qc = ((local_channel()) ? get_pconfig(local_channel(),'system','qcomment') : null);
$qcomment = (($qc) ? explode("\n",$qc) : null);
$arr = array('comment_buttons' => '','id' => $this->get_id());
call_hooks('comment_buttons',$arr);
$comment_buttons = $arr['comment_buttons'];
$feature_auto_save_draft = ((feature_enabled($conv->get_profile_owner(), 'auto_save_draft')) ? "true" : "false");
$comment_box = replace_macros($template,array(
'$return_path' => '',
'$threaded' => $this->is_threaded(),
'$jsreload' => $conv->reload,
'$jsreload' => '', //(($conv->get_mode() === 'display') ? $_SESSION['return_url'] : ''),
'$type' => (($conv->get_mode() === 'channel') ? 'wall-comment' : 'net-comment'),
'$id' => $this->get_id(),
'$parent' => $this->get_id(),
'$qcomment' => $qcomment,
'$comment_buttons' => $comment_buttons,
'$profile_uid' => $conv->get_profile_owner(),
'$mylink' => $observer['xchan_url'],
@@ -761,22 +729,15 @@ class ThreadItem {
'$edquote' => t('Quote'),
'$edcode' => t('Code'),
'$edimg' => t('Image'),
'$edatt' => t('Attach/Upload file'),
'$edurl' => t('Insert Link'),
'$edvideo' => t('Video'),
'$preview' => t('Preview'), // ((feature_enabled($conv->get_profile_owner(),'preview')) ? t('Preview') : ''),
'$indent' => $indent,
'$can_upload' => (perm_is_allowed($conv->get_profile_owner(),get_observer_hash(),'write_storage') && $conv->is_uploadable()),
'$feature_encrypt' => ((feature_enabled($conv->get_profile_owner(),'content_encrypt')) ? true : false),
'$encrypt' => t('Encrypt text'),
'$cipher' => $conv->get_cipher(),
'$sourceapp' => \App::$sourcename,
'$observer' => get_observer_hash(),
'$anoncomments' => ((($conv->get_mode() === 'channel' || $conv->get_mode() === 'display') && perm_is_allowed($conv->get_profile_owner(),'','post_comments')) ? true : false),
'$anonname' => [ 'anonname', t('Your full name (required)') ],
'$anonmail' => [ 'anonmail', t('Your email address (required)') ],
'$anonurl' => [ 'anonurl', t('Your website URL (optional)') ],
'$auto_save_draft' => $feature_auto_save_draft,
'$sourceapp' => \App::$sourcename
));
return $comment_box;
@@ -800,7 +761,7 @@ class ThreadItem {
return;
if($this->is_toplevel() && ($this->get_data_value('author_xchan') != $this->get_data_value('owner_xchan'))) {
$this->owner_url = chanlink_hash($this->data['owner']['xchan_hash']);
$this->owner_url = chanlink_url($this->data['owner']['xchan_url']);
$this->owner_photo = $this->data['owner']['xchan_photo_m'];
$this->owner_name = $this->data['owner']['xchan_name'];
$this->wall_to_wall = true;

View File

@@ -18,21 +18,18 @@ class ThreadStream {
private $observer = null;
private $writable = false;
private $commentable = false;
private $uploadable = false;
private $profile_owner = 0;
private $preview = false;
private $prepared_item = '';
public $reload = '';
private $cipher = 'aes256';
// $prepared_item is for use by alternate conversation structures such as photos
// wherein we've already prepared a top level item which doesn't look anything like
// a normal "post" item
public function __construct($mode, $preview, $uploadable, $prepared_item = '') {
public function __construct($mode, $preview, $prepared_item = '') {
$this->set_mode($mode);
$this->preview = $preview;
$this->uploadable = $uploadable;
$this->prepared_item = $prepared_item;
$c = ((local_channel()) ? get_pconfig(local_channel(),'system','default_cipher') : '');
if($c)
@@ -54,34 +51,15 @@ class ThreadStream {
$this->profile_owner = local_channel();
$this->writable = true;
break;
case 'pubstream':
$this->profile_owner = local_channel();
$this->writable = ((local_channel()) ? true : false);
break;
case 'hq':
$this->profile_owner = local_channel();
$this->writable = true;
break;
case 'channel':
$this->profile_owner = \App::$profile['profile_uid'];
$this->writable = perm_is_allowed($this->profile_owner,$ob_hash,'post_comments');
break;
case 'cards':
$this->profile_owner = \App::$profile['profile_uid'];
$this->writable = perm_is_allowed($this->profile_owner,$ob_hash,'post_comments');
$this->reload = $_SESSION['return_url'];
break;
case 'articles':
$this->profile_owner = \App::$profile['profile_uid'];
$this->writable = perm_is_allowed($this->profile_owner,$ob_hash,'post_comments');
$this->reload = $_SESSION['return_url'];
break;
case 'display':
// in this mode we set profile_owner after initialisation (from conversation()) and then
// pull some trickery which allows us to re-invoke this function afterward
// it's an ugly hack so @FIXME
// it's an ugly hack so FIXME
$this->writable = perm_is_allowed($this->profile_owner,$ob_hash,'post_comments');
$this->uploadable = perm_is_allowed($this->profile_owner,$ob_hash,'write_storage');
break;
case 'page':
$this->profile_owner = \App::$profile['uid'];
@@ -113,11 +91,6 @@ class ThreadStream {
return $this->commentable;
}
public function is_uploadable() {
return $this->uploadable;
}
/**
* Check if page is a preview
*/
@@ -185,17 +158,13 @@ class ThreadStream {
if(intval($item->get_data_value('item_nocomment'))) {
$item->set_commentable(false);
}
elseif(! $item->is_commentable()) {
elseif(($this->observer) && (! $item->is_commentable())) {
if((array_key_exists('owner',$item->data)) && intval($item->data['owner']['abook_self']))
$item->set_commentable(perm_is_allowed($this->profile_owner,$ob_hash,'post_comments'));
$item->set_commentable(perm_is_allowed($this->profile_owner,$this->observer['xchan_hash'],'post_comments'));
else
$item->set_commentable(can_comment_on_post($ob_hash,$item->data));
$item->set_commentable(can_comment_on_post($this->observer['xchan_hash'],$item->data));
}
}
if($this->mode === 'pubstream' && (! local_channel())) {
$item->set_commentable(false);
}
require_once('include/channel.php');
$item->set_conversation($this);

View File

@@ -1,63 +0,0 @@
<?php
namespace Zotlabs\Lib;
class Verify {
function create($type,$channel_id,$token,$meta) {
return q("insert into verify ( vtype, channel, token, meta, created ) values ( '%s', %d, '%s', '%s', '%s' )",
dbesc($type),
intval($channel_id),
dbesc($token),
dbesc($meta),
dbesc(datetime_convert())
);
}
function match($type,$channel_id,$token,$meta) {
$r = q("select id from verify where vtype = '%s' and channel = %d and token = '%s' and meta = '%s' limit 1",
dbesc($type),
intval($channel_id),
dbesc($token),
dbesc($meta)
);
if($r) {
q("delete from verify where id = %d",
intval($r[0]['id'])
);
return true;
}
return false;
}
function get_meta($type,$channel_id,$token) {
$r = q("select id, meta from verify where vtype = '%s' and channel = %d and token = '%s' limit 1",
dbesc($type),
intval($channel_id),
dbesc($token)
);
if($r) {
q("delete from verify where id = %d",
intval($r[0]['id'])
);
return $r[0]['meta'];
}
return false;
}
/**
* @brief Purge entries of a verify-type older than interval.
*
* @param string $type Verify type
* @param string $interval SQL compatible time interval
*/
function purge($type, $interval) {
q("delete from verify where vtype = '%s' and created < %s - INTERVAL %s",
dbesc($type),
db_utcnow(),
db_quoteinterval($interval)
);
}
}

View File

@@ -2,26 +2,7 @@
namespace Zotlabs\Lib;
/**
* @brief Class for handling observer's config.
*
* <b>XConfig</b> is comparable to <i>PConfig</i>, except that it uses <i>xchan</i>
* (an observer hash) as an identifier.
*
* <b>XConfig</b> is used for observer specific configurations and takes a
* <i>xchan</i> as identifier.
* The storage is of size MEDIUMTEXT.
*
* @code{.php}$var = Zotlabs\Lib\XConfig::Get('xchan', 'category', 'key');
* // with default value for non existent key
* $var = Zotlabs\Lib\XConfig::Get('xchan', 'category', 'unsetkey', 'defaultvalue');@endcode
*
* The old (deprecated?) way to access a XConfig value is:
* @code{.php}$observer = App::get_observer_hash();
* if ($observer) {
* $var = get_xconfig($observer, 'category', 'key');
* }@endcode
*/
class XConfig {
/**
@@ -34,6 +15,7 @@ class XConfig {
* The observer's hash
* @return void|false Returns false if xchan is not set
*/
static public function Load($xchan) {
if(! $xchan)
@@ -74,21 +56,21 @@ class XConfig {
* The category of the configuration value
* @param string $key
* The configuration key to query
* @param boolean $default (optional) default false
* @return mixed Stored $value or false if it does not exist
*/
static public function Get($xchan, $family, $key, $default = false) {
static public function Get($xchan, $family, $key) {
if(! $xchan)
return $default;
return false;
if(! array_key_exists($xchan, \App::$config))
load_xconfig($xchan);
if((! array_key_exists($family, \App::$config[$xchan])) || (! array_key_exists($key, \App::$config[$xchan][$family])))
return $default;
return false;
return ((! is_array(\App::$config[$xchan][$family][$key])) && (preg_match('|^a:[0-9]+:{.*}$|s', \App::$config[$xchan][$family][$key]))
return ((! is_array(\App::$config[$xchan][$family][$key])) && (preg_match('|^a:[0-9]+:{.*}$|s', \App::$config[$xchan][$family][$key]))
? unserialize(\App::$config[$xchan][$family][$key])
: \App::$config[$xchan][$family][$key]
);
@@ -100,6 +82,7 @@ class XConfig {
* Stores a config value ($value) in the category ($family) under the key ($key)
* for the observer's $xchan hash.
*
*
* @param string $xchan
* The observer's hash
* @param string $family
@@ -110,6 +93,7 @@ class XConfig {
* The value to store
* @return mixed Stored $value or false
*/
static public function Set($xchan, $family, $key, $value) {
// manage array value
@@ -122,7 +106,7 @@ class XConfig {
if(! array_key_exists($family, \App::$config[$xchan]))
\App::$config[$xchan][$family] = array();
$ret = q("INSERT INTO xconfig ( xchan, cat, k, v ) VALUES ( '%s', '%s', '%s', '%s' )",
$ret = q("INSERT INTO xconfig ( xchan, cat, k, v ) VALUES ( '%s', '%s', '%s', '%s' ) ",
dbesc($xchan),
dbesc($family),
dbesc($key),
@@ -142,7 +126,6 @@ class XConfig {
if($ret)
return $value;
return $ret;
}
@@ -160,11 +143,11 @@ class XConfig {
* The configuration key to delete
* @return mixed
*/
static public function Delete($xchan, $family, $key) {
if(x(\App::$config[$xchan][$family], $key))
unset(\App::$config[$xchan][$family][$key]);
$ret = q("DELETE FROM xconfig WHERE xchan = '%s' AND cat = '%s' AND k = '%s'",
dbesc($xchan),
dbesc($family),

30
Zotlabs/Lib/ZotDriver.php Normal file
View File

@@ -0,0 +1,30 @@
<?php /** @file */
namespace Zotlabs\Lib;
class ZotDriver extends ProtoDriver {
protected function discover($channel,$location) {
}
protected function deliver($item,$channel,$recipients) {
}
protected function collect($channel,$connection) {
}
protected function change_permissions($permissions,$channel,$recipient) {
}
protected function acknowledge_permissions($permissions,$channel,$recipient) {
}
protected function deliver_private($item,$channel,$recipients) {
}
protected function collect_private($channel,$connection) {
}
}

View File

@@ -1,42 +1,38 @@
<?php
namespace Zotlabs\Module;
require_once 'include/acl_selectors.php';
require_once 'include/group.php';
/**
* @brief ACL selector json backend.
*
/*
* ACL selector json backend
* This module provides JSON lists of connections and local/remote channels
* (xchans) to populate various tools such as the ACL (AccessControlList) popup
* and various auto-complete functions (such as email recipients, search, and
* and various auto-complete functions (such as email recipients, search, and
* mention targets.
*
* There are two primary output structural formats. One for the ACL widget and
* the other for auto-completion.
*
* Many of the behaviour variations are triggered on the use of single character
* keys however this functionality has grown in an ad-hoc manner and has gotten
* quite messy over time.
* Many of the behaviour variations are triggered on the use of single character keys
* however this functionality has grown in an ad-hoc manner and has gotten quite messy over time.
*/
require_once("include/acl_selectors.php");
require_once("include/group.php");
class Acl extends \Zotlabs\Web\Controller {
function init() {
// logger('mod_acl: ' . print_r($_GET,true),LOGGER_DATA);
function init(){
// logger('mod_acl: ' . print_r($_REQUEST,true));
$start = (x($_REQUEST,'start') ? $_REQUEST['start'] : 0);
$count = (x($_REQUEST,'count') ? $_REQUEST['count'] : 500);
$search = (x($_REQUEST,'search') ? $_REQUEST['search'] : '');
$type = (x($_REQUEST,'type') ? $_REQUEST['type'] : '');
$noforums = (x($_REQUEST,'n') ? $_REQUEST['n'] : false);
$noforums = (x($_REQUEST,'n') ? $_REQUEST['n'] : false);
// $type =
// $type =
// '' => standard ACL request
// 'g' => Groups only ACL request
// 'f' => forums only ACL request
// 'c' => Connections only ACL request or editor (textarea) mention request
// $_REQUEST['search'] contains ACL search text.
@@ -53,19 +49,19 @@ class Acl extends \Zotlabs\Web\Controller {
$extra_channels = (x($_REQUEST,'extra_channels') ? $_REQUEST['extra_channels'] : array());
// The different autocomplete libraries use different names for the search text
// parameter. Internally we'll use $search to represent the search text no matter
// parameter. Internaly we'll use $search to represent the search text no matter
// what request variable it was attached to.
if(array_key_exists('query',$_REQUEST)) {
$search = $_REQUEST['query'];
}
if( (! local_channel()) && (! in_array($type, [ 'x', 'c', 'f' ])))
if( (! local_channel()) && (! ($type == 'x' || $type == 'c')))
killme();
$permitted = [];
if(in_array($type, [ 'm', 'a', 'c', 'f' ])) {
if(in_array($type, [ 'm', 'a', 'c' ])) {
// These queries require permission checking. We'll create a simple array of xchan_hash for those with
// the requisite permissions which we can check against.
@@ -81,8 +77,8 @@ class Acl extends \Zotlabs\Web\Controller {
if($search) {
$sql_extra = " AND groups.gname LIKE " . protect_sprintf( "'%" . dbesc($search) . "%'" ) . " ";
$sql_extra2 = "AND ( xchan_name LIKE " . protect_sprintf( "'%" . dbesc($search) . "%'" ) . " OR xchan_addr LIKE " . protect_sprintf( "'%" . dbesc(punify($search)) . ((strpos($search,'@') === false) ? "%@%'" : "%'")) . ") ";
$sql_extra = " AND `name` LIKE " . protect_sprintf( "'%" . dbesc($search) . "%'" ) . " ";
$sql_extra2 = "AND ( xchan_name LIKE " . protect_sprintf( "'%" . dbesc($search) . "%'" ) . " OR xchan_addr LIKE " . protect_sprintf( "'%" . dbesc($search) . ((strpos($search,'@') === false) ? "%@%'" : "%'")) . ") ";
// This horrible mess is needed because position also returns 0 if nothing is found.
// Would be MUCH easier if it instead returned a very large value
@@ -91,10 +87,11 @@ class Acl extends \Zotlabs\Web\Controller {
$order_extra2 = "CASE WHEN xchan_name LIKE "
. protect_sprintf( "'%" . dbesc($search) . "%'" )
. " then POSITION('" . protect_sprintf(dbesc($search))
. "' IN xchan_name) else position('" . protect_sprintf(dbesc(punify($search))) . "' IN xchan_addr) end, ";
. " then POSITION('" . dbesc($search)
. "' IN xchan_name) else position('" . dbesc($search) . "' IN xchan_addr) end, ";
$sql_extra3 = "AND ( xchan_addr like " . protect_sprintf( "'%" . dbesc(punify($search)) . "%'" ) . " OR xchan_name like " . protect_sprintf( "'%" . dbesc($search) . "%'" ) . " ) ";
$col = ((strpos($search,'@') !== false) ? 'xchan_addr' : 'xchan_name' );
$sql_extra3 = "AND $col like " . protect_sprintf( "'%" . dbesc($search) . "%'" ) . " ";
}
else {
@@ -106,32 +103,11 @@ class Acl extends \Zotlabs\Web\Controller {
$contacts = array();
if($type == '' || $type == 'g') {
// virtual groups based on private profile viewing ability
$r = q("select id, profile_guid, profile_name from profile where is_default = 0 and uid = %d",
intval(local_channel())
);
if($r) {
foreach($r as $rv) {
$groups[] = array(
"type" => "g",
"photo" => "images/twopeople.png",
"name" => t('Profile','acl') . ' ' . $rv['profile_name'],
"id" => 'vp' . $rv['id'],
"xid" => 'vp.' . $rv['profile_guid'],
"uids" => group_get_profile_members_xchan(local_channel(), $rv['id']),
"link" => ''
);
}
}
// Normal privacy groups
$r = q("SELECT groups.id, groups.hash, groups.gname
FROM groups, group_member
FROM groups,group_member
WHERE groups.deleted = 0 AND groups.uid = %d
AND group_member.gid = groups.id
AND group_member.gid=groups.id
$sql_extra
GROUP BY groups.id
ORDER BY groups.gname
@@ -157,42 +133,26 @@ class Acl extends \Zotlabs\Web\Controller {
}
}
if($type == '' || $type == 'c' || $type === 'f') {
if($type == '' || $type == 'c') {
$extra_channels_sql = '';
// Only include channels who allow the observer to view their connections
if($extra_channels) {
foreach($extra_channels as $channel) {
if(perm_is_allowed(intval($channel), get_observer_hash(),'view_contacts')) {
if($extra_channel_sql)
$extra_channels_sql .= ',';
$extra_channels_sql .= intval($channel);
}
}
// Only include channels who allow the observer to view their permissions
foreach($extra_channels as $channel) {
if(perm_is_allowed(intval($channel), get_observer_hash(),'view_contacts'))
$extra_channels_sql .= "," . intval($channel);
}
$extra_channels_sql = substr($extra_channels_sql,1); // Remove initial comma
// Getting info from the abook is better for local users because it contains info about permissions
if(local_channel()) {
if($extra_channels_sql != '')
$extra_channels_sql = " OR (abook_channel IN ($extra_channels_sql)) and abook_hidden = 0 ";
// Add atokens belonging to the local channel
if($search) {
$sql_extra_atoken = "AND ( atoken_name LIKE " . protect_sprintf( "'%" . dbesc($search) . "%'" ) . ") ";
}
else {
$sql_extra_atoken = '';
}
$r2 = null;
$r1 = q("select * from atoken where atoken_uid = %d $sql_extra_atoken",
$r1 = q("select * from atoken where atoken_uid = %d",
intval(local_channel())
);
if($r1) {
require_once('include/security.php');
$r2 = array();
@@ -212,7 +172,6 @@ class Acl extends \Zotlabs\Web\Controller {
}
}
// add connections
$r = q("SELECT abook_id as id, xchan_hash as hash, xchan_name as name, xchan_photo_s as micro, xchan_url as url, xchan_addr as nick, abook_their_perms, xchan_pubforum, abook_flags, abook_self
FROM abook left join xchan on abook_xchan = xchan_hash
@@ -267,15 +226,15 @@ class Acl extends \Zotlabs\Web\Controller {
});
}
}
if((count($r) < 100) && $type == 'c') {
$r2 = q("SELECT substr(xchan_hash,1,18) as id, xchan_hash as hash, xchan_name as name, xchan_photo_s as micro, xchan_url as url, xchan_addr as nick, 0 as abook_their_perms, 0 as abook_flags, 0 as abook_self
FROM xchan
WHERE xchan_deleted = 0 and not xchan_network in ('rss','anon','unknown') $sql_extra2 order by $order_extra2 xchan_name asc"
);
if($r2) {
$r = array_merge($r,$r2);
$r = unique_multidim_array($r,'hash');
}
if(intval(get_config('system','taganyone')) || intval(get_pconfig(local_channel(),'system','taganyone'))) {
if((count($r) < 100) && $type == 'c') {
$r2 = q("SELECT substr(xchan_hash,1,18) as id, xchan_hash as hash, xchan_name as name, xchan_photo_s as micro, xchan_url as url, xchan_addr as nick, 0 as abook_their_perms, 0 as abook_flags, 0 as abook_self
FROM xchan
WHERE xchan_deleted = 0 $sql_extra2 order by $order_extra2 xchan_name asc"
);
if($r2)
$r = array_merge($r,$r2);
}
}
}
elseif($type == 'm') {
@@ -317,7 +276,7 @@ class Acl extends \Zotlabs\Web\Controller {
$contacts[] = array(
"photo" => $g['photo'],
"name" => $g['name'],
"nick" => $g['address']
"nick" => $g['address'],
);
}
}
@@ -334,45 +293,38 @@ class Acl extends \Zotlabs\Web\Controller {
$r = array();
if($r) {
foreach($r as $g) {
foreach($r as $g){
if(in_array($g['network'],['rss','anon','unknown']) && ($type != 'a'))
// remove RSS feeds from ACLs - they are inaccessible
if(strpos($g['hash'],'/') && $type != 'a')
continue;
$g['hash'] = urlencode($g['hash']);
if(! $g['nick']) {
$g['nick'] = $g['url'];
}
if(in_array($g['hash'],$permitted) && $type === 'f' && (! $noforums)) {
if(in_array($g['hash'],$permitted) && $type == 'c' && (! $noforums)) {
$contacts[] = array(
"type" => "c",
"photo" => "images/twopeople.png",
"name" => $g['name'],
"id" => urlencode($g['id']),
"name" => $g['name'] . '+',
"id" => $g['id'] . '+',
"xid" => $g['hash'],
"link" => (($g['nick']) ? $g['nick'] : $g['url']),
"link" => $g['nick'],
"nick" => substr($g['nick'],0,strpos($g['nick'],'@')),
"self" => (intval($g['abook_self']) ? 'abook-self' : ''),
"taggable" => 'taggable',
"label" => t('network')
);
}
if($type !== 'f') {
$contacts[] = array(
"type" => "c",
"photo" => $g['micro'],
"name" => $g['name'],
"id" => urlencode($g['id']),
"xid" => $g['hash'],
"link" => (($g['nick']) ? $g['nick'] : $g['url']),
"nick" => ((strpos($g['nick'],'@')) ? substr($g['nick'],0,strpos($g['nick'],'@')) : $g['nick']),
"self" => (intval($g['abook_self']) ? 'abook-self' : ''),
"taggable" => '',
"label" => '',
);
}
$contacts[] = array(
"type" => "c",
"photo" => $g['micro'],
"name" => $g['name'],
"id" => $g['id'],
"xid" => $g['hash'],
"link" => $g['nick'],
"nick" => (($g['nick']) ? substr($g['nick'],0,strpos($g['nick'],'@')) : t('RSS')),
"self" => (intval($g['abook_self']) ? 'abook-self' : ''),
"taggable" => '',
"label" => '',
);
}
}
@@ -383,13 +335,15 @@ class Acl extends \Zotlabs\Web\Controller {
'count' => $count,
'items' => $items,
);
echo json_encode($o);
killme();
}
function navbar_complete(&$a) {
// logger('navbar_complete');
@@ -427,13 +381,11 @@ class Acl extends \Zotlabs\Web\Controller {
$directory = find_upstream_directory($dirmode);
$url = $directory['url'] . '/dirsearch';
}
$token = get_config('system','realm_token');
$count = (x($_REQUEST,'count') ? $_REQUEST['count'] : 100);
if($url) {
$query = $url . '?f=' . (($token) ? '&t=' . urlencode($token) : '');
$query .= '&name=' . urlencode($search) . "&limit=$count" . (($address) ? '&address=' . urlencode(punify($search)) : '');
$query = $url . '?f=' ;
$query .= '&name=' . urlencode($search) . "&limit=$count" . (($address) ? '&address=' . urlencode($search) : '');
$x = z_fetch_url($query);
if($x['success']) {
@@ -446,5 +398,5 @@ class Acl extends \Zotlabs\Web\Controller {
}
return array();
}
}

View File

@@ -1,20 +1,21 @@
<?php
namespace Zotlabs\Module;
/**
* @file Zotlabs/Module/Admin.php
* @file mod/admin.php
* @brief Hubzilla's admin controller.
*
* Controller for the /admin/ area.
*/
namespace Zotlabs\Module;
require_once('include/queue_fn.php');
require_once('include/account.php');
/**
* @brief Admin area.
*
* @param App &$a
*/
class Admin extends \Zotlabs\Web\Controller {
private $sm = null;
@@ -25,37 +26,36 @@ class Admin extends \Zotlabs\Web\Controller {
function post(){
logger('admin_post', LOGGER_DEBUG);
if(! is_site_admin()) {
return;
}
if (argc() > 1) {
$this->sm->call('post');
}
goaway(z_root() . '/admin' );
}
/**
* @return string
*/
function get() {
logger('admin_content', LOGGER_DEBUG);
if(! is_site_admin()) {
return login(false);
}
/*
* Page content
*/
nav_set_selected('Admin');
$o = '';
if(argc() > 1) {
$o = $this->sm->call('get');
if($o === false) {
@@ -65,9 +65,9 @@ class Admin extends \Zotlabs\Web\Controller {
else {
$o = $this->admin_page_summary();
}
if(is_ajax()) {
echo $o;
echo $o;
killme();
return '';
}
@@ -75,15 +75,16 @@ class Admin extends \Zotlabs\Web\Controller {
return $o;
}
}
/**
* @brief Returns content for Admin Summary Page.
*
* @param App &$a
* @return string HTML from parsed admin_summary.tpl
*/
function admin_page_summary() {
// list total user accounts, expirations etc.
$accounts = array();
$r = q("SELECT COUNT(*) AS total, COUNT(CASE WHEN account_expires > %s THEN 1 ELSE NULL END) AS expiring, COUNT(CASE WHEN account_expires < %s AND account_expires > '%s' THEN 1 ELSE NULL END) AS expired, COUNT(CASE WHEN (account_flags & %d)>0 THEN 1 ELSE NULL END) AS blocked FROM account",
@@ -93,48 +94,48 @@ class Admin extends \Zotlabs\Web\Controller {
intval(ACCOUNT_BLOCKED)
);
if ($r) {
$accounts['total'] = array('label' => t('Accounts'), 'val' => $r[0]['total']);
$accounts['blocked'] = array('label' => t('Blocked accounts'), 'val' => $r[0]['blocked']);
$accounts['expired'] = array('label' => t('Expired accounts'), 'val' => $r[0]['expired']);
$accounts['expiring'] = array('label' => t('Expiring accounts'), 'val' => $r[0]['expiring']);
$accounts['total'] = array('label' => t('# Accounts'), 'val' => $r[0]['total']);
$accounts['blocked'] = array('label' => t('# blocked accounts'), 'val' => $r[0]['blocked']);
$accounts['expired'] = array('label' => t('# expired accounts'), 'val' => $r[0]['expired']);
$accounts['expiring'] = array('label' => t('# expiring accounts'), 'val' => $r[0]['expiring']);
}
// pending registrations
$pdg = q("SELECT account.*, register.hash from account left join register on account_id = register.uid where (account_flags & %d ) > 0 ",
intval(ACCOUNT_PENDING)
);
$pending = (($pdg) ? count($pdg) : 0);
$r = q("SELECT COUNT(id) AS `count` FROM `register` WHERE `uid` != '0'");
$pending = $r[0]['count'];
// available channels, primary and clones
$channels = array();
$r = q("SELECT COUNT(*) AS total, COUNT(CASE WHEN channel_primary = 1 THEN 1 ELSE NULL END) AS main, COUNT(CASE WHEN channel_primary = 0 THEN 1 ELSE NULL END) AS clones FROM channel WHERE channel_removed = 0");
if ($r) {
$channels['total'] = array('label' => t('Channels'), 'val' => $r[0]['total']);
$channels['main'] = array('label' => t('Primary'), 'val' => $r[0]['main']);
$channels['clones'] = array('label' => t('Clones'), 'val' => $r[0]['clones']);
$channels['total'] = array('label' => t('# Channels'), 'val' => $r[0]['total']);
$channels['main'] = array('label' => t('# primary'), 'val' => $r[0]['main']);
$channels['clones'] = array('label' => t('# clones'), 'val' => $r[0]['clones']);
}
// We can do better, but this is a quick queue status
$r = q("SELECT COUNT(outq_delivered) AS total FROM outq WHERE outq_delivered = 0");
$queue = (($r) ? $r[0]['total'] : 0);
$queues = array( 'label' => t('Message queues'), 'queue' => $queue );
// If no plugins active return 0, otherwise list of plugin names
$plugins = (count(\App::$plugins) == 0) ? count(\App::$plugins) : \App::$plugins;
if(is_array($plugins))
sort($plugins);
// Could be extended to provide also other alerts to the admin
$alertmsg = '';
// annoy admin about upcoming unsupported PHP version
if (version_compare(PHP_VERSION, '5.4', '<')) {
$alertmsg = 'Your PHP version ' . PHP_VERSION . ' will not be supported with the next major release of $Projectname. You are strongly urged to upgrade to a current version.'
. '<br>PHP 5.3 has reached its <a href="http://php.net/eol.php" class="alert-link">End of Life (EOL)</a> in August 2014.'
. ' A list about current PHP versions can be found <a href="http://php.net/supported-versions.php" class="alert-link">here</a>.';
}
$vmaster = get_repository_version('master');
$vdev = get_repository_version('dev');
$upgrade = ((version_compare(STD_VERSION,$vmaster) < 0) ? t('Your software should be updated') : '');
$t = get_markup_template('admin_summary.tpl');
return replace_macros($t, array(
'$title' => t('Administration'),
@@ -144,13 +145,15 @@ class Admin extends \Zotlabs\Web\Controller {
'$accounts' => array( t('Registered accounts'), $accounts),
'$pending' => array( t('Pending registrations'), $pending),
'$channels' => array( t('Registered channels'), $channels),
'$plugins' => array( t('Active addons'), $plugins ),
'$plugins' => array( t('Active plugins'), $plugins ),
'$version' => array( t('Version'), STD_VERSION),
'$vmaster' => array( t('Repository version (master)'), $vmaster),
'$vdev' => array( t('Repository version (dev)'), $vdev),
'$upgrade' => $upgrade,
'$build' => get_config('system', 'db_version')
'$build' => get_config('system', 'db_version')
));
}
}

View File

@@ -29,22 +29,6 @@ class Account_edit {
info( sprintf( t('Password changed for account %d.'), $account_id). EOL);
}
$service_class = trim($_REQUEST['service_class']);
$account_level = intval(trim($_REQUEST['account_level']));
$account_language = trim($_REQUEST['account_language']);
$r = q("update account set account_service_class = '%s', account_level = %d, account_language = '%s'
where account_id = %d",
dbesc($service_class),
intval($account_level),
dbesc($account_language),
intval($account_id)
);
if($r)
info( t('Account settings updated.') . EOL);
goaway(z_root() . '/admin/accounts');
}
@@ -62,15 +46,11 @@ class Account_edit {
return '';
}
$a = replace_macros(get_markup_template('admin_account_edit.tpl'), [
'$account' => $x[0],
'$title' => t('Account Edit'),
'$pass1' => [ 'pass1', t('New Password'), ' ','' ],
'$pass2' => [ 'pass2', t('New Password again'), ' ','' ],
'$account_level' => [ 'account_level', t('Technical skill level'), $x[0]['account_level'], '', \Zotlabs\Lib\Techlevels::levels() ],
'$account_language' => [ 'account_language' , t('Account language (for emails)'), $x[0]['account_language'], '', language_list() ],
'$service_class' => [ 'service_class', t('Service class'), $x[0]['account_service_class'], '' ],
'$submit' => t('Submit'),
]
);

View File

@@ -16,7 +16,6 @@ class Accounts {
*/
function post() {
$pending = ( x($_POST, 'pending') ? $_POST['pending'] : array() );
$users = ( x($_POST, 'user') ? $_POST['user'] : array() );
$blocked = ( x($_POST, 'blocked') ? $_POST['blocked'] : array() );
@@ -25,7 +24,7 @@ class Accounts {
// change to switch structure?
// account block/unblock button was submitted
if (x($_POST, 'page_accounts_block')) {
if (x($_POST, 'page_users_block')) {
for ($i = 0; $i < count($users); $i++) {
// if account is blocked remove blocked bit-flag, otherwise add blocked bit-flag
$op = ($blocked[$i]) ? '& ~' : '| ';
@@ -44,13 +43,13 @@ class Accounts {
notice( sprintf( tt("%s account deleted", "%s accounts deleted", count($users)), count($users)) );
}
// registration approved button was submitted
if (x($_POST, 'page_accounts_approve')) {
if (x($_POST, 'page_users_approve')) {
foreach ($pending as $hash) {
account_allow($hash);
}
}
// registration deny button was submitted
if (x($_POST, 'page_accounts_deny')) {
if (x($_POST, 'page_users_deny')) {
foreach ($pending as $hash) {
account_deny($hash);
}
@@ -133,13 +132,13 @@ class Accounts {
$base = z_root() . '/admin/accounts?f=';
$odir = (($dir === 'asc') ? '0' : '1');
$users = q("SELECT account_id , account_email, account_lastlog, account_created, account_expires, account_service_class, ( account_flags & %d ) > 0 as blocked,
(SELECT %s FROM channel as ch WHERE ch.channel_account_id = ac.account_id and ch.channel_removed = 0 ) as channels FROM account as ac
where true $serviceclass and account_flags != %d order by $key $dir limit %d offset %d ",
$users = q("SELECT `account_id` , `account_email`, `account_lastlog`, `account_created`, `account_expires`, " . "`account_service_class`, ( account_flags & %d ) > 0 as `blocked`, " .
"(SELECT %s FROM channel as ch " .
"WHERE ch.channel_account_id = ac.account_id and ch.channel_removed = 0 ) as `channels` " .
"FROM account as ac where true $serviceclass order by $key $dir limit %d offset %d ",
intval(ACCOUNT_BLOCKED),
db_concat('ch.channel_address', ' '),
intval(ACCOUNT_BLOCKED | ACCOUNT_PENDING),
intval(\App::$pager['itemspage']),
intval(\App::$pager['start'])
);
@@ -204,4 +203,4 @@ class Accounts {
}
}
}

View File

@@ -1,479 +0,0 @@
<?php
namespace Zotlabs\Module\Admin;
use \Zotlabs\Storage\GitRepo;
use \Michelf\MarkdownExtra;
class Addons {
/**
* @brief
*
*/
function post() {
if(argc() > 2 && is_file("addon/" . argv(2) . "/" . argv(2) . ".php")) {
@include_once("addon/" . argv(2) . "/" . argv(2) . ".php");
if(function_exists(argv(2).'_plugin_admin_post')) {
$func = argv(2) . '_plugin_admin_post';
$func($a);
}
goaway(z_root() . '/admin/addons/' . argv(2) );
}
elseif(argc() > 2) {
switch(argv(2)) {
case 'updaterepo':
if (array_key_exists('repoName', $_REQUEST)) {
$repoName = $_REQUEST['repoName'];
}
else {
json_return_and_die(array('message' => 'No repo name provided.', 'success' => false));
}
$extendDir = 'store/[data]/git/sys/extend';
$addonDir = $extendDir . '/addon';
if (!file_exists($extendDir)) {
if (!mkdir($extendDir, 0770, true)) {
logger('Error creating extend folder: ' . $extendDir);
json_return_and_die(array('message' => 'Error creating extend folder: ' . $extendDir, 'success' => false));
}
else {
if (!symlink(realpath('extend/addon'), $addonDir)) {
logger('Error creating symlink to addon folder: ' . $addonDir);
json_return_and_die(array('message' => 'Error creating symlink to addon folder: ' . $addonDir, 'success' => false));
}
}
}
$repoDir = 'store/[data]/git/sys/extend/addon/' . $repoName;
if (!is_dir($repoDir)) {
logger('Repo directory does not exist: ' . $repoDir);
json_return_and_die(array('message' => 'Invalid addon repo.', 'success' => false));
}
if (!is_writable($repoDir)) {
logger('Repo directory not writable to web server: ' . $repoDir);
json_return_and_die(array('message' => 'Repo directory not writable to web server.', 'success' => false));
}
$git = new GitRepo('sys', null, false, $repoName, $repoDir);
try {
if ($git->pull()) {
$files = array_diff(scandir($repoDir), array('.', '..'));
foreach ($files as $file) {
if (is_dir($repoDir . '/' . $file) && $file !== '.git') {
$source = '../extend/addon/' . $repoName . '/' . $file;
$target = realpath('addon/') . '/' . $file;
unlink($target);
if (!symlink($source, $target)) {
logger('Error linking addons to /addon');
json_return_and_die(array('message' => 'Error linking addons to /addon', 'success' => false));
}
}
}
json_return_and_die(array('message' => 'Repo updated.', 'success' => true));
} else {
json_return_and_die(array('message' => 'Error updating addon repo.', 'success' => false));
}
} catch (\PHPGit\Exception\GitException $e) {
json_return_and_die(array('message' => 'Error updating addon repo.', 'success' => false));
}
case 'removerepo':
if (array_key_exists('repoName', $_REQUEST)) {
$repoName = $_REQUEST['repoName'];
} else {
json_return_and_die(array('message' => 'No repo name provided.', 'success' => false));
}
$extendDir = 'store/[data]/git/sys/extend';
$addonDir = $extendDir . '/addon';
if (!file_exists($extendDir)) {
if (!mkdir($extendDir, 0770, true)) {
logger('Error creating extend folder: ' . $extendDir);
json_return_and_die(array('message' => 'Error creating extend folder: ' . $extendDir, 'success' => false));
} else {
if (!symlink(realpath('extend/addon'), $addonDir)) {
logger('Error creating symlink to addon folder: ' . $addonDir);
json_return_and_die(array('message' => 'Error creating symlink to addon folder: ' . $addonDir, 'success' => false));
}
}
}
$repoDir = 'store/[data]/git/sys/extend/addon/' . $repoName;
if (!is_dir($repoDir)) {
logger('Repo directory does not exist: ' . $repoDir);
json_return_and_die(array('message' => 'Invalid addon repo.', 'success' => false));
}
if (!is_writable($repoDir)) {
logger('Repo directory not writable to web server: ' . $repoDir);
json_return_and_die(array('message' => 'Repo directory not writable to web server.', 'success' => false));
}
/// @TODO remove directory and unlink /addon/files
if (rrmdir($repoDir)) {
json_return_and_die(array('message' => 'Repo deleted.', 'success' => true));
} else {
json_return_and_die(array('message' => 'Error deleting addon repo.', 'success' => false));
}
case 'installrepo':
if (array_key_exists('repoURL', $_REQUEST)) {
require_once('library/PHPGit.autoload.php'); // Load PHPGit dependencies
$repoURL = $_REQUEST['repoURL'];
$extendDir = 'store/[data]/git/sys/extend';
$addonDir = $extendDir . '/addon';
if (!file_exists($extendDir)) {
if (!mkdir($extendDir, 0770, true)) {
logger('Error creating extend folder: ' . $extendDir);
json_return_and_die(array('message' => 'Error creating extend folder: ' . $extendDir, 'success' => false));
} else {
if (!symlink(realpath('extend/addon'), $addonDir)) {
logger('Error creating symlink to addon folder: ' . $addonDir);
json_return_and_die(array('message' => 'Error creating symlink to addon folder: ' . $addonDir, 'success' => false));
}
}
}
if (!is_writable($extendDir)) {
logger('Directory not writable to web server: ' . $extendDir);
json_return_and_die(array('message' => 'Directory not writable to web server.', 'success' => false));
}
$repoName = null;
if (array_key_exists('repoName', $_REQUEST) && $_REQUEST['repoName'] !== '') {
$repoName = $_REQUEST['repoName'];
} else {
$repoName = GitRepo::getRepoNameFromURL($repoURL);
}
if (!$repoName) {
logger('Invalid git repo');
json_return_and_die(array('message' => 'Invalid git repo', 'success' => false));
}
$repoDir = $addonDir . '/' . $repoName;
$tempRepoBaseDir = 'store/[data]/git/sys/temp/';
$tempAddonDir = $tempRepoBaseDir . $repoName;
if (!is_writable($addonDir) || !is_writable($tempAddonDir)) {
logger('Temp repo directory or /extend/addon not writable to web server: ' . $tempAddonDir);
json_return_and_die(array('message' => 'Temp repo directory not writable to web server.', 'success' => false));
}
rename($tempAddonDir, $repoDir);
if (!is_writable(realpath('addon/'))) {
logger('/addon directory not writable to web server: ' . $tempAddonDir);
json_return_and_die(array('message' => '/addon directory not writable to web server.', 'success' => false));
}
$files = array_diff(scandir($repoDir), array('.', '..'));
foreach ($files as $file) {
if (is_dir($repoDir . '/' . $file) && $file !== '.git') {
$source = '../extend/addon/' . $repoName . '/' . $file;
$target = realpath('addon/') . '/' . $file;
unlink($target);
if (!symlink($source, $target)) {
logger('Error linking addons to /addon');
json_return_and_die(array('message' => 'Error linking addons to /addon', 'success' => false));
}
}
}
$git = new GitRepo('sys', $repoURL, false, $repoName, $repoDir);
$repo = $git->probeRepo();
json_return_and_die(array('repo' => $repo, 'message' => '', 'success' => true));
}
case 'addrepo':
if (array_key_exists('repoURL', $_REQUEST)) {
require_once('library/PHPGit.autoload.php'); // Load PHPGit dependencies
$repoURL = $_REQUEST['repoURL'];
$extendDir = 'store/[data]/git/sys/extend';
$addonDir = $extendDir . '/addon';
$tempAddonDir = realpath('store/[data]') . '/git/sys/temp';
if (!file_exists($extendDir)) {
if (!mkdir($extendDir, 0770, true)) {
logger('Error creating extend folder: ' . $extendDir);
json_return_and_die(array('message' => 'Error creating extend folder: ' . $extendDir, 'success' => false));
} else {
if (!symlink(realpath('extend/addon'), $addonDir)) {
logger('Error creating symlink to addon folder: ' . $addonDir);
json_return_and_die(array('message' => 'Error creating symlink to addon folder: ' . $addonDir, 'success' => false));
}
}
}
if (!is_dir($tempAddonDir)) {
if (!mkdir($tempAddonDir, 0770, true)) {
logger('Error creating temp plugin repo folder: ' . $tempAddonDir);
json_return_and_die(array('message' => 'Error creating temp plugin repo folder: ' . $tempAddonDir, 'success' => false));
}
}
$repoName = null;
if (array_key_exists('repoName', $_REQUEST) && $_REQUEST['repoName'] !== '') {
$repoName = $_REQUEST['repoName'];
} else {
$repoName = GitRepo::getRepoNameFromURL($repoURL);
}
if (!$repoName) {
logger('Invalid git repo');
json_return_and_die(array('message' => 'Invalid git repo: ' . $repoName, 'success' => false));
}
$repoDir = $tempAddonDir . '/' . $repoName;
if (!is_writable($tempAddonDir)) {
logger('Temporary directory for new addon repo is not writable to web server: ' . $tempAddonDir);
json_return_and_die(array('message' => 'Temporary directory for new addon repo is not writable to web server.', 'success' => false));
}
// clone the repo if new automatically
$git = new GitRepo('sys', $repoURL, true, $repoName, $repoDir);
$remotes = $git->git->remote();
$fetchURL = $remotes['origin']['fetch'];
if ($fetchURL !== $git->url) {
if (rrmdir($repoDir)) {
$git = new GitRepo('sys', $repoURL, true, $repoName, $repoDir);
} else {
json_return_and_die(array('message' => 'Error deleting existing addon repo.', 'success' => false));
}
}
$repo = $git->probeRepo();
$repo['readme'] = $repo['manifest'] = null;
foreach ($git->git->tree('master') as $object) {
if ($object['type'] == 'blob' && (strtolower($object['file']) === 'readme.md' || strtolower($object['file']) === 'readme')) {
$repo['readme'] = MarkdownExtra::defaultTransform($git->git->cat->blob($object['hash']));
} else if ($object['type'] == 'blob' && strtolower($object['file']) === 'manifest.json') {
$repo['manifest'] = $git->git->cat->blob($object['hash']);
}
}
json_return_and_die(array('repo' => $repo, 'message' => '', 'success' => true));
} else {
json_return_and_die(array('message' => 'No repo URL provided', 'success' => false));
}
break;
default:
break;
}
}
}
/**
* @brief Addons admin page.
*
* @return string with parsed HTML
*/
function get() {
/*
* Single plugin
*/
if (\App::$argc == 3){
$plugin = \App::$argv[2];
if (!is_file("addon/$plugin/$plugin.php")){
notice( t("Item not found.") );
return '';
}
$enabled = in_array($plugin,\App::$plugins);
$info = get_plugin_info($plugin);
$x = check_plugin_versions($info);
// disable plugins which are installed but incompatible versions
if($enabled && ! $x) {
$enabled = false;
$idz = array_search($plugin, \App::$plugins);
if ($idz !== false) {
unset(\App::$plugins[$idz]);
uninstall_plugin($plugin);
set_config("system","addon", implode(", ",\App::$plugins));
}
}
$info['disabled'] = 1-intval($x);
if (x($_GET,"a") && $_GET['a']=="t"){
check_form_security_token_redirectOnErr('/admin/addons', 'admin_addons', 't');
$pinstalled = false;
// Toggle plugin status
$idx = array_search($plugin, \App::$plugins);
if ($idx !== false){
unset(\App::$plugins[$idx]);
uninstall_plugin($plugin);
$pinstalled = false;
info( sprintf( t("Plugin %s disabled."), $plugin ) );
} else {
\App::$plugins[] = $plugin;
install_plugin($plugin);
$pinstalled = true;
info( sprintf( t("Plugin %s enabled."), $plugin ) );
}
set_config("system","addon", implode(", ",\App::$plugins));
if($pinstalled) {
@require_once("addon/$plugin/$plugin.php");
if(function_exists($plugin.'_plugin_admin'))
goaway(z_root() . '/admin/addons/' . $plugin);
}
goaway(z_root() . '/admin/addons' );
}
// display plugin details
if (in_array($plugin, \App::$plugins)){
$status = 'on';
$action = t('Disable');
} else {
$status = 'off';
$action = t('Enable');
}
$readme = null;
if (is_file("addon/$plugin/README.md")){
$readme = file_get_contents("addon/$plugin/README.md");
$readme = MarkdownExtra::defaultTransform($readme);
} else if (is_file("addon/$plugin/README")){
$readme = "<pre>". file_get_contents("addon/$plugin/README") ."</pre>";
}
$admin_form = '';
$r = q("select * from addon where plugin_admin = 1 and aname = '%s' limit 1",
dbesc($plugin)
);
if($r) {
@require_once("addon/$plugin/$plugin.php");
if(function_exists($plugin.'_plugin_admin')) {
$func = $plugin.'_plugin_admin';
$func($a, $admin_form);
}
}
$t = get_markup_template('admin_plugins_details.tpl');
return replace_macros($t, array(
'$title' => t('Administration'),
'$page' => t('Addons'),
'$toggle' => t('Toggle'),
'$settings' => t('Settings'),
'$baseurl' => z_root(),
'$plugin' => $plugin,
'$status' => $status,
'$action' => $action,
'$info' => $info,
'$str_author' => t('Author: '),
'$str_maintainer' => t('Maintainer: '),
'$str_minversion' => t('Minimum project version: '),
'$str_maxversion' => t('Maximum project version: '),
'$str_minphpversion' => t('Minimum PHP version: '),
'$str_serverroles' => t('Compatible Server Roles: '),
'$str_requires' => t('Requires: '),
'$disabled' => t('Disabled - version incompatibility'),
'$admin_form' => $admin_form,
'$function' => 'addons',
'$screenshot' => '',
'$readme' => $readme,
'$form_security_token' => get_form_security_token('admin_addons'),
));
}
/*
* List plugins
*/
$plugins = array();
$files = glob('addon/*/');
if($files) {
foreach($files as $file) {
if (is_dir($file)){
list($tmp, $id) = array_map('trim', explode('/', $file));
$info = get_plugin_info($id);
$enabled = in_array($id,\App::$plugins);
$x = check_plugin_versions($info);
// disable plugins which are installed but incompatible versions
if($enabled && ! $x) {
$enabled = false;
$idz = array_search($id, \App::$plugins);
if ($idz !== false) {
unset(\App::$plugins[$idz]);
uninstall_plugin($id);
set_config("system","addon", implode(", ",\App::$plugins));
}
}
$info['disabled'] = 1-intval($x);
$plugins[] = array( $id, (($enabled)?"on":"off") , $info);
}
}
}
usort($plugins,'self::plugin_sort');
$allowManageRepos = false;
if(is_writable('extend/addon') && is_writable('store/[data]')) {
$allowManageRepos = true;
}
$admin_plugins_add_repo_form= replace_macros(
get_markup_template('admin_plugins_addrepo.tpl'), array(
'$post' => 'admin/addons/addrepo',
'$desc' => t('Enter the public git repository URL of the addon repo.'),
'$repoURL' => array('repoURL', t('Addon repo git URL'), '', ''),
'$repoName' => array('repoName', t('Custom repo name'), '', '', t('(optional)')),
'$submit' => t('Download Addon Repo')
)
);
$newRepoModalID = random_string(3);
$newRepoModal = replace_macros(
get_markup_template('generic_modal.tpl'), array(
'$id' => $newRepoModalID,
'$title' => t('Install new repo'),
'$ok' => t('Install'),
'$cancel' => t('Cancel')
)
);
$reponames = $this->listAddonRepos();
$addonrepos = [];
foreach($reponames as $repo) {
$addonrepos[] = array('name' => $repo, 'description' => '');
/// @TODO Parse repo info to provide more information about repos
}
$t = get_markup_template('admin_plugins.tpl');
return replace_macros($t, array(
'$title' => t('Administration'),
'$page' => t('Addons'),
'$submit' => t('Submit'),
'$baseurl' => z_root(),
'$function' => 'addons',
'$plugins' => $plugins,
'$disabled' => t('Disabled - version incompatibility'),
'$form_security_token' => get_form_security_token('admin_addons'),
'$allowManageRepos' => $allowManageRepos,
'$managerepos' => t('Manage Repos'),
'$installedtitle' => t('Installed Addon Repositories'),
'$addnewrepotitle' => t('Install a New Addon Repository'),
'$expandform' => false,
'$form' => $admin_plugins_add_repo_form,
'$newRepoModal' => $newRepoModal,
'$newRepoModalID' => $newRepoModalID,
'$addonrepos' => $addonrepos,
'$repoUpdateButton' => t('Update'),
'$repoBranchButton' => t('Switch branch'),
'$repoRemoveButton' => t('Remove')
));
}
function listAddonRepos() {
$addonrepos = [];
$addonDir = 'extend/addon/';
if(is_dir($addonDir)) {
if ($handle = opendir($addonDir)) {
while (false !== ($entry = readdir($handle))) {
if ($entry != "." && $entry != "..") {
$addonrepos[] = $entry;
}
}
closedir($handle);
}
}
return $addonrepos;
}
static public function plugin_sort($a,$b) {
return(strcmp(strtolower($a[2]['name']),strtolower($b[2]['name'])));
}
}

View File

@@ -2,36 +2,35 @@
namespace Zotlabs\Module\Admin;
/**
* @brief Admin Module for Channels.
*
*/
class Channels {
/**
* @brief Handle POST actions on channels admin page.
* @brief Channels admin page.
*
* @param App &$a
*/
function post() {
$channels = ( x($_POST, 'channel') ? $_POST['channel'] : Array() );
check_form_security_token_redirectOnErr('/admin/channels', 'admin_channels');
$xor = db_getfunc('^');
if(x($_POST, 'page_channels_block')) {
foreach($channels as $uid) {
if (x($_POST,'page_channels_block')){
foreach($channels as $uid){
q("UPDATE channel SET channel_pageflags = ( channel_pageflags $xor %d ) where channel_id = %d",
intval(PAGE_CENSORED),
intval( $uid )
);
\Zotlabs\Daemon\Master::Summon(array('Directory', $uid, 'nopush'));
\Zotlabs\Daemon\Master::Summon(array('Directory',$uid,'nopush'));
}
notice( sprintf( tt("%s channel censored/uncensored", "%s channels censored/uncensored", count($channels)), count($channels)) );
}
if(x($_POST, 'page_channels_code')) {
foreach($channels as $uid) {
if (x($_POST,'page_channels_code')){
foreach($channels as $uid){
q("UPDATE channel SET channel_pageflags = ( channel_pageflags $xor %d ) where channel_id = %d",
intval(PAGE_ALLOWCODE),
intval( $uid )
@@ -39,71 +38,74 @@ class Channels {
}
notice( sprintf( tt("%s channel code allowed/disallowed", "%s channels code allowed/disallowed", count($channels)), count($channels)) );
}
if(x($_POST, 'page_channels_delete')) {
foreach($channels as $uid) {
channel_remove($uid, true);
if (x($_POST,'page_channels_delete')){
foreach($channels as $uid){
channel_remove($uid,true);
}
notice( sprintf( tt("%s channel deleted", "%s channels deleted", count($channels)), count($channels)) );
}
goaway(z_root() . '/admin/channels' );
}
/**
* @brief Generate channels admin page and handle single item operations.
* @brief
*
* @return string with parsed HTML
* @return string
*/
function get() {
if(argc() > 2) {
$uid = argv(3);
$channel = q("SELECT * FROM channel WHERE channel_id = %d",
intval($uid)
);
if(! $channel) {
notice( t('Channel not found') . EOL);
goaway(z_root() . '/admin/channels' );
}
switch(argv(2)) {
case "delete":{
check_form_security_token_redirectOnErr('/admin/channels', 'admin_channels', 't');
// delete channel
channel_remove($uid,true);
notice( sprintf(t("Channel '%s' deleted"), $channel[0]['channel_name']) . EOL);
}; break;
case "block":{
check_form_security_token_redirectOnErr('/admin/channels', 'admin_channels', 't');
$pflags = $channel[0]['channel_pageflags'] ^ PAGE_CENSORED;
$pflags = $channel[0]['channel_pageflags'] ^ PAGE_CENSORED;
q("UPDATE channel SET channel_pageflags = %d where channel_id = %d",
intval($pflags),
intval( $uid )
);
\Zotlabs\Daemon\Master::Summon(array('Directory',$uid,'nopush'));
notice( sprintf( (($pflags & PAGE_CENSORED) ? t("Channel '%s' censored"): t("Channel '%s' uncensored")) , $channel[0]['channel_name'] . ' (' . $channel[0]['channel_address'] . ')' ) . EOL);
}; break;
case "code":{
check_form_security_token_redirectOnErr('/admin/channels', 'admin_channels', 't');
$pflags = $channel[0]['channel_pageflags'] ^ PAGE_ALLOWCODE;
$pflags = $channel[0]['channel_pageflags'] ^ PAGE_ALLOWCODE;
q("UPDATE channel SET channel_pageflags = %d where channel_id = %d",
intval($pflags),
intval( $uid )
);
notice( sprintf( (($pflags & PAGE_ALLOWCODE) ? t("Channel '%s' code allowed"): t("Channel '%s' code disallowed")) , $channel[0]['channel_name'] . ' (' . $channel[0]['channel_address'] . ')' ) . EOL);
}; break;
default:
default:
break;
}
goaway(z_root() . '/admin/channels' );
}
$key = (($_REQUEST['key']) ? dbesc($_REQUEST['key']) : 'channel_id');
$dir = 'asc';
if(array_key_exists('dir',$_REQUEST))
@@ -112,8 +114,10 @@ class Channels {
$base = z_root() . '/admin/channels?f=';
$odir = (($dir === 'asc') ? '0' : '1');
/* get channels */
/* get channels */
$total = q("SELECT count(*) as total FROM channel where channel_removed = 0 and channel_system = 0");
if($total) {
\App::set_pager_total($total[0]['total']);
@@ -131,15 +135,15 @@ class Channels {
$channels[$x]['blocked'] = true;
else
$channels[$x]['blocked'] = false;
if($channels[$x]['channel_pageflags'] & PAGE_ALLOWCODE)
$channels[$x]['allowcode'] = true;
else
$channels[$x]['allowcode'] = false;
}
}
$t = get_markup_template('admin_channels.tpl');
$t = get_markup_template("admin_channels.tpl");
$o = replace_macros($t, array(
// strings //
'$title' => t('Administration'),
@@ -154,23 +158,29 @@ class Channels {
'$h_channels' => t('Channel'),
'$base' => $base,
'$odir' => $odir,
'$th_channels' => array(
'$th_channels' => array(
[ t('UID'), 'channel_id' ],
[ t('Name'), 'channel_name' ],
[ t('Address'), 'channel_address' ]),
'$confirm_delete_multi' => t('Selected channels will be deleted!\n\nEverything that was posted in these channels on this site will be permanently deleted!\n\nAre you sure?'),
'$confirm_delete' => t('The channel {0} will be deleted!\n\nEverything that was posted in this channel on this site will be permanently deleted!\n\nAre you sure?'),
'$form_security_token' => get_form_security_token('admin_channels'),
'$form_security_token' => get_form_security_token("admin_channels"),
// values //
'$baseurl' => z_root(),
'$channels' => $channels,
));
$o .= paginate($a);
return $o;
}
}

View File

@@ -7,65 +7,61 @@ namespace Zotlabs\Module\Admin;
class Dbsync {
function get() {
$o = '';
if(argc() > 3 && intval(argv(3)) && argv(2) === 'mark') {
// remove the old style config if it exists
del_config('database', 'update_r' . intval(argv(3)));
set_config('database', '_' . intval(argv(3)), 'success');
if(intval(get_config('system','db_version')) < intval(argv(3)))
set_config('system','db_version',intval(argv(3)));
set_config('database', 'update_r' . intval(argv(3)), 'success');
if(intval(get_config('system','db_version')) <= intval(argv(3)))
set_config('system','db_version',intval(argv(3)) + 1);
info( t('Update has been marked successful') . EOL);
goaway(z_root() . '/admin/dbsync');
}
if(argc() > 2 && intval(argv(2))) {
$x = intval(argv(2));
$s = '_' . $x;
$cls = '\\Zotlabs\Update\\' . $s ;
if(class_exists($cls)) {
$c = new $cls();
$retval = $c->run();
require_once('install/update.php');
$func = 'update_r' . intval(argv(2));
if(function_exists($func)) {
$retval = $func();
if($retval === UPDATE_FAILED) {
$o .= sprintf( t('Executing %s failed. Check system logs.'), $s);
$o .= sprintf( t('Executing %s failed. Check system logs.'), $func);
}
elseif($retval === UPDATE_SUCCESS) {
$o .= sprintf( t('Update %s was successfully applied.'), $s);
set_config('database',$s, 'success');
$o .= sprintf( t('Update %s was successfully applied.'), $func);
set_config('database',$func, 'success');
}
else
$o .= sprintf( t('Update %s did not return a status. Unknown if it succeeded.'), $s);
$o .= sprintf( t('Update %s did not return a status. Unknown if it succeeded.'), $func);
}
else
$o .= sprintf( t('Update function %s could not be found.'), $s);
$o .= sprintf( t('Update function %s could not be found.'), $func);
return $o;
}
$failed = array();
$r = q("select * from config where cat = 'database' ");
$r = q("select * from config where `cat` = 'database' ");
if(count($r)) {
foreach($r as $rr) {
$upd = intval(substr($rr['k'],-4));
$upd = intval(substr($rr['k'],8));
if($rr['v'] === 'success')
continue;
$failed[] = $upd;
}
}
if(count($failed)) {
$o = replace_macros(get_markup_template('failed_updates.tpl'),array(
'$base' => z_root(),
'$banner' => t('Failed Updates'),
'$desc' => '',
'$mark' => t('Mark success (if update was manually applied)'),
'$apply' => t('Attempt to execute this update step automatically'),
'$failed' => $failed
));
}
else {
if(! count($failed))
return '<div class="generic-content-wrapper-styled"><h3>' . t('No failed updates.') . '</h3></div>';
}
$o = replace_macros(get_markup_template('failed_updates.tpl'),array(
'$base' => z_root(),
'$banner' => t('Failed Updates'),
'$desc' => '',
'$mark' => t('Mark success (if update was manually applied)'),
'$apply' => t('Attempt to execute this update step automatically'),
'$failed' => $failed
));
return $o;
}

View File

@@ -0,0 +1,470 @@
<?php
namespace Zotlabs\Module\Admin;
use \Zotlabs\Storage\GitRepo as GitRepo;
class Plugins {
function post() {
if(argc() > 2 && is_file("addon/" . argv(2) . "/" . argv(2) . ".php")) {
@include_once("addon/" . argv(2) . "/" . argv(2) . ".php");
if(function_exists(argv(2).'_plugin_admin_post')) {
$func = argv(2) . '_plugin_admin_post';
$func($a);
}
goaway(z_root() . '/admin/plugins/' . argv(2) );
}
elseif(argc() > 2) {
switch(argv(2)) {
case 'updaterepo':
if (array_key_exists('repoName', $_REQUEST)) {
$repoName = $_REQUEST['repoName'];
}
else {
json_return_and_die(array('message' => 'No repo name provided.', 'success' => false));
}
$extendDir = 'store/[data]/git/sys/extend';
$addonDir = $extendDir . '/addon';
if (!file_exists($extendDir)) {
if (!mkdir($extendDir, 0770, true)) {
logger('Error creating extend folder: ' . $extendDir);
json_return_and_die(array('message' => 'Error creating extend folder: ' . $extendDir, 'success' => false));
}
else {
if (!symlink('extend/addon', $addonDir)) {
logger('Error creating symlink to addon folder: ' . $addonDir);
json_return_and_die(array('message' => 'Error creating symlink to addon folder: ' . $addonDir, 'success' => false));
}
}
}
$repoDir = 'store/[data]/git/sys/extend/addon/' . $repoName;
if (!is_dir($repoDir)) {
logger('Repo directory does not exist: ' . $repoDir);
json_return_and_die(array('message' => 'Invalid addon repo.', 'success' => false));
}
if (!is_writable($repoDir)) {
logger('Repo directory not writable to web server: ' . $repoDir);
json_return_and_die(array('message' => 'Repo directory not writable to web server.', 'success' => false));
}
$git = new GitRepo('sys', null, false, $repoName, $repoDir);
try {
if ($git->pull()) {
$files = array_diff(scandir($repoDir), array('.', '..'));
foreach ($files as $file) {
if (is_dir($repoDir . '/' . $file) && $file !== '.git') {
$source = 'extend/addon/' . $repoName . '/' . $file;
$target = realpath('addon/') . '/' . $file;
unlink($target);
if (!symlink($source, $target)) {
logger('Error linking addons to /addon');
json_return_and_die(array('message' => 'Error linking addons to /addon', 'success' => false));
}
}
}
json_return_and_die(array('message' => 'Repo updated.', 'success' => true));
} else {
json_return_and_die(array('message' => 'Error updating addon repo.', 'success' => false));
}
} catch (\PHPGit\Exception\GitException $e) {
json_return_and_die(array('message' => 'Error updating addon repo.', 'success' => false));
}
case 'removerepo':
if (array_key_exists('repoName', $_REQUEST)) {
$repoName = $_REQUEST['repoName'];
} else {
json_return_and_die(array('message' => 'No repo name provided.', 'success' => false));
}
$extendDir = 'store/[data]/git/sys/extend';
$addonDir = $extendDir . '/addon';
if (!file_exists($extendDir)) {
if (!mkdir($extendDir, 0770, true)) {
logger('Error creating extend folder: ' . $extendDir);
json_return_and_die(array('message' => 'Error creating extend folder: ' . $extendDir, 'success' => false));
} else {
if (!symlink('extend/addon', $addonDir)) {
logger('Error creating symlink to addon folder: ' . $addonDir);
json_return_and_die(array('message' => 'Error creating symlink to addon folder: ' . $addonDir, 'success' => false));
}
}
}
$repoDir = 'store/[data]/git/sys/extend/addon/' . $repoName;
if (!is_dir($repoDir)) {
logger('Repo directory does not exist: ' . $repoDir);
json_return_and_die(array('message' => 'Invalid addon repo.', 'success' => false));
}
if (!is_writable($repoDir)) {
logger('Repo directory not writable to web server: ' . $repoDir);
json_return_and_die(array('message' => 'Repo directory not writable to web server.', 'success' => false));
}
// TODO: remove directory and unlink /addon/files
if (rrmdir($repoDir)) {
json_return_and_die(array('message' => 'Repo deleted.', 'success' => true));
} else {
json_return_and_die(array('message' => 'Error deleting addon repo.', 'success' => false));
}
case 'installrepo':
require_once('library/markdown.php');
if (array_key_exists('repoURL', $_REQUEST)) {
require_once('library/PHPGit.autoload.php'); // Load PHPGit dependencies
$repoURL = $_REQUEST['repoURL'];
$extendDir = 'store/[data]/git/sys/extend';
$addonDir = $extendDir . '/addon';
if (!file_exists($extendDir)) {
if (!mkdir($extendDir, 0770, true)) {
logger('Error creating extend folder: ' . $extendDir);
json_return_and_die(array('message' => 'Error creating extend folder: ' . $extendDir, 'success' => false));
} else {
if (!symlink('extend/addon', $addonDir)) {
logger('Error creating symlink to addon folder: ' . $addonDir);
json_return_and_die(array('message' => 'Error creating symlink to addon folder: ' . $addonDir, 'success' => false));
}
}
}
if (!is_writable($extendDir)) {
logger('Directory not writable to web server: ' . $extendDir);
json_return_and_die(array('message' => 'Directory not writable to web server.', 'success' => false));
}
$repoName = null;
if (array_key_exists('repoName', $_REQUEST) && $_REQUEST['repoName'] !== '') {
$repoName = $_REQUEST['repoName'];
} else {
$repoName = GitRepo::getRepoNameFromURL($repoURL);
}
if (!$repoName) {
logger('Invalid git repo');
json_return_and_die(array('message' => 'Invalid git repo', 'success' => false));
}
$repoDir = $addonDir . '/' . $repoName;
$tempRepoBaseDir = 'store/[data]/git/sys/temp/';
$tempAddonDir = $tempRepoBaseDir . $repoName;
if (!is_writable($addonDir) || !is_writable($tempAddonDir)) {
logger('Temp repo directory or /extend/addon not writable to web server: ' . $tempAddonDir);
json_return_and_die(array('message' => 'Temp repo directory not writable to web server.', 'success' => false));
}
rename($tempAddonDir, $repoDir);
if (!is_writable(realpath('addon/'))) {
logger('/addon directory not writable to web server: ' . $tempAddonDir);
json_return_and_die(array('message' => '/addon directory not writable to web server.', 'success' => false));
}
$files = array_diff(scandir($repoDir), array('.', '..'));
foreach ($files as $file) {
if (is_dir($repoDir . '/' . $file) && $file !== '.git') {
$source = 'extend/addon/' . $repoName . '/' . $file;
$target = realpath('addon/') . '/' . $file;
unlink($target);
if (!symlink($source, $target)) {
logger('Error linking addons to /addon');
json_return_and_die(array('message' => 'Error linking addons to /addon', 'success' => false));
}
}
}
$git = new GitRepo('sys', $repoURL, false, $repoName, $repoDir);
$repo = $git->probeRepo();
json_return_and_die(array('repo' => $repo, 'message' => '', 'success' => true));
}
case 'addrepo':
require_once('library/markdown.php');
if (array_key_exists('repoURL', $_REQUEST)) {
require_once('library/PHPGit.autoload.php'); // Load PHPGit dependencies
$repoURL = $_REQUEST['repoURL'];
$extendDir = 'store/[data]/git/sys/extend';
$addonDir = $extendDir . '/addon';
$tempAddonDir = 'store/[data]/git/sys/temp';
if (!file_exists($extendDir)) {
if (!mkdir($extendDir, 0770, true)) {
logger('Error creating extend folder: ' . $extendDir);
json_return_and_die(array('message' => 'Error creating extend folder: ' . $extendDir, 'success' => false));
} else {
if (!symlink('extend/addon', $addonDir)) {
logger('Error creating symlink to addon folder: ' . $addonDir);
json_return_and_die(array('message' => 'Error creating symlink to addon folder: ' . $addonDir, 'success' => false));
}
}
}
if (!is_dir($tempAddonDir)) {
if (!mkdir($tempAddonDir, 0770, true)) {
logger('Error creating temp plugin repo folder: ' . $tempAddonDir);
json_return_and_die(array('message' => 'Error creating temp plugin repo folder: ' . $tempAddonDir, 'success' => false));
}
}
$repoName = null;
if (array_key_exists('repoName', $_REQUEST) && $_REQUEST['repoName'] !== '') {
$repoName = $_REQUEST['repoName'];
} else {
$repoName = GitRepo::getRepoNameFromURL($repoURL);
}
if (!$repoName) {
logger('Invalid git repo');
json_return_and_die(array('message' => 'Invalid git repo: ' . $repoName, 'success' => false));
}
$repoDir = $tempAddonDir . '/' . $repoName;
if (!is_writable($tempAddonDir)) {
logger('Temporary directory for new addon repo is not writable to web server: ' . $tempAddonDir);
json_return_and_die(array('message' => 'Temporary directory for new addon repo is not writable to web server.', 'success' => false));
}
// clone the repo if new automatically
$git = new GitRepo('sys', $repoURL, true, $repoName, $repoDir);
$remotes = $git->git->remote();
$fetchURL = $remotes['origin']['fetch'];
if ($fetchURL !== $git->url) {
if (rrmdir($repoDir)) {
$git = new GitRepo('sys', $repoURL, true, $repoName, $repoDir);
} else {
json_return_and_die(array('message' => 'Error deleting existing addon repo.', 'success' => false));
}
}
$repo = $git->probeRepo();
$repo['readme'] = $repo['manifest'] = null;
foreach ($git->git->tree('master') as $object) {
if ($object['type'] == 'blob' && (strtolower($object['file']) === 'readme.md' || strtolower($object['file']) === 'readme')) {
$repo['readme'] = Markdown($git->git->cat->blob($object['hash']));
} else if ($object['type'] == 'blob' && strtolower($object['file']) === 'manifest.json') {
$repo['manifest'] = $git->git->cat->blob($object['hash']);
}
}
json_return_and_die(array('repo' => $repo, 'message' => '', 'success' => true));
} else {
json_return_and_die(array('message' => 'No repo URL provided', 'success' => false));
}
break;
default:
break;
}
}
}
function get() {
/*
* Single plugin
*/
if (\App::$argc == 3){
$plugin = \App::$argv[2];
if (!is_file("addon/$plugin/$plugin.php")){
notice( t("Item not found.") );
return '';
}
$enabled = in_array($plugin,\App::$plugins);
$info = get_plugin_info($plugin);
$x = check_plugin_versions($info);
// disable plugins which are installed but incompatible versions
if($enabled && ! $x) {
$enabled = false;
$idz = array_search($plugin, \App::$plugins);
if ($idz !== false) {
unset(\App::$plugins[$idz]);
uninstall_plugin($plugin);
set_config("system","addon", implode(", ",\App::$plugins));
}
}
$info['disabled'] = 1-intval($x);
if (x($_GET,"a") && $_GET['a']=="t"){
check_form_security_token_redirectOnErr('/admin/plugins', 'admin_plugins', 't');
$pinstalled = false;
// Toggle plugin status
$idx = array_search($plugin, \App::$plugins);
if ($idx !== false){
unset(\App::$plugins[$idx]);
uninstall_plugin($plugin);
$pinstalled = false;
info( sprintf( t("Plugin %s disabled."), $plugin ) );
} else {
\App::$plugins[] = $plugin;
install_plugin($plugin);
$pinstalled = true;
info( sprintf( t("Plugin %s enabled."), $plugin ) );
}
set_config("system","addon", implode(", ",\App::$plugins));
if($pinstalled) {
@require_once("addon/$plugin/$plugin.php");
if(function_exists($plugin.'_plugin_admin'))
goaway(z_root() . '/admin/plugins/' . $plugin);
}
goaway(z_root() . '/admin/plugins' );
}
// display plugin details
require_once('library/markdown.php');
if (in_array($plugin, \App::$plugins)){
$status = 'on';
$action = t('Disable');
} else {
$status = 'off';
$action = t('Enable');
}
$readme = null;
if (is_file("addon/$plugin/README.md")){
$readme = file_get_contents("addon/$plugin/README.md");
$readme = Markdown($readme);
} else if (is_file("addon/$plugin/README")){
$readme = "<pre>". file_get_contents("addon/$plugin/README") ."</pre>";
}
$admin_form = '';
$r = q("select * from addon where plugin_admin = 1 and aname = '%s' limit 1",
dbesc($plugin)
);
if($r) {
@require_once("addon/$plugin/$plugin.php");
if(function_exists($plugin.'_plugin_admin')) {
$func = $plugin.'_plugin_admin';
$func($a, $admin_form);
}
}
$t = get_markup_template('admin_plugins_details.tpl');
return replace_macros($t, array(
'$title' => t('Administration'),
'$page' => t('Plugins'),
'$toggle' => t('Toggle'),
'$settings' => t('Settings'),
'$baseurl' => z_root(),
'$plugin' => $plugin,
'$status' => $status,
'$action' => $action,
'$info' => $info,
'$str_author' => t('Author: '),
'$str_maintainer' => t('Maintainer: '),
'$str_minversion' => t('Minimum project version: '),
'$str_maxversion' => t('Maximum project version: '),
'$str_minphpversion' => t('Minimum PHP version: '),
'$str_serverroles' => t('Compatible Server Roles: '),
'$str_requires' => t('Requires: '),
'$disabled' => t('Disabled - version incompatibility'),
'$admin_form' => $admin_form,
'$function' => 'plugins',
'$screenshot' => '',
'$readme' => $readme,
'$form_security_token' => get_form_security_token('admin_plugins'),
));
}
/*
* List plugins
*/
$plugins = array();
$files = glob('addon/*/');
if($files) {
foreach($files as $file) {
if (is_dir($file)){
list($tmp, $id) = array_map('trim', explode('/', $file));
$info = get_plugin_info($id);
$enabled = in_array($id,\App::$plugins);
$x = check_plugin_versions($info);
// disable plugins which are installed but incompatible versions
if($enabled && ! $x) {
$enabled = false;
$idz = array_search($id, \App::$plugins);
if ($idz !== false) {
unset(\App::$plugins[$idz]);
uninstall_plugin($id);
set_config("system","addon", implode(", ",\App::$plugins));
}
}
$info['disabled'] = 1-intval($x);
$plugins[] = array( $id, (($enabled)?"on":"off") , $info);
}
}
}
usort($plugins,'self::plugin_sort');
$admin_plugins_add_repo_form= replace_macros(
get_markup_template('admin_plugins_addrepo.tpl'), array(
'$post' => 'admin/plugins/addrepo',
'$desc' => t('Enter the public git repository URL of the plugin repo.'),
'$repoURL' => array('repoURL', t('Plugin repo git URL'), '', ''),
'$repoName' => array('repoName', t('Custom repo name'), '', '', t('(optional)')),
'$submit' => t('Download Plugin Repo')
)
);
$newRepoModalID = random_string(3);
$newRepoModal = replace_macros(
get_markup_template('generic_modal.tpl'), array(
'$id' => $newRepoModalID,
'$title' => t('Install new repo'),
'$ok' => t('Install'),
'$cancel' => t('Cancel')
)
);
$reponames = $this->listAddonRepos();
$addonrepos = [];
foreach($reponames as $repo) {
$addonrepos[] = array('name' => $repo, 'description' => '');
// TODO: Parse repo info to provide more information about repos
}
$t = get_markup_template('admin_plugins.tpl');
return replace_macros($t, array(
'$title' => t('Administration'),
'$page' => t('Plugins'),
'$submit' => t('Submit'),
'$baseurl' => z_root(),
'$function' => 'plugins',
'$plugins' => $plugins,
'$disabled' => t('Disabled - version incompatibility'),
'$form_security_token' => get_form_security_token('admin_plugins'),
'$managerepos' => t('Manage Repos'),
'$installedtitle' => t('Installed Plugin Repositories'),
'$addnewrepotitle' => t('Install a New Plugin Repository'),
'$expandform' => false,
'$form' => $admin_plugins_add_repo_form,
'$newRepoModal' => $newRepoModal,
'$newRepoModalID' => $newRepoModalID,
'$addonrepos' => $addonrepos,
'$repoUpdateButton' => t('Update'),
'$repoBranchButton' => t('Switch branch'),
'$repoRemoveButton' => t('Remove')
));
}
function listAddonRepos() {
$addonrepos = [];
$addonDir = 'extend/addon/';
if(is_dir($addonDir)) {
if ($handle = opendir($addonDir)) {
while (false !== ($entry = readdir($handle))) {
if ($entry != "." && $entry != "..") {
$addonrepos[] = $entry;
}
}
closedir($handle);
}
}
return $addonrepos;
}
static public function plugin_sort($a,$b) {
return(strcmp(strtolower($a[2]['name']),strtolower($b[2]['name'])));
}
}

View File

@@ -9,37 +9,17 @@ class Profs {
if(array_key_exists('basic',$_REQUEST)) {
$arr = explode(',',$_REQUEST['basic']);
array_walk($arr,'array_trim');
$narr = [];
if(count($arr)) {
foreach($arr as $a) {
if(strlen($a)) {
$narr[] = $a;
}
}
}
if(! $narr)
del_config('system','profile_fields_basic');
else
set_config('system','profile_fields_basic',$narr);
for($x = 0; $x < count($arr); $x ++)
if(trim($arr[$x]))
$arr[$x] = trim($arr[$x]);
set_config('system','profile_fields_basic',$arr);
if(array_key_exists('advanced',$_REQUEST)) {
$arr = explode(',',$_REQUEST['advanced']);
array_walk($arr,'array_trim');
$narr = [];
if(count($arr)) {
foreach($arr as $a) {
if(strlen($a)) {
$narr[] = $a;
}
}
}
if(! $narr)
del_config('system','profile_fields_advanced');
else
set_config('system','profile_fields_advanced',$narr);
for($x = 0; $x < count($arr); $x ++)
if(trim($arr[$x]))
$arr[$x] = trim($arr[$x]);
set_config('system','profile_fields_advanced',$arr);
}
goaway(z_root() . '/admin/profs');
}
@@ -118,7 +98,6 @@ class Profs {
$basic = '';
$barr = array();
$fields = get_profile_fields_basic();
if(! $fields)
$fields = get_profile_fields_basic(1);
if($fields) {

View File

@@ -15,6 +15,7 @@ class Queue {
$expert = ((array_key_exists('expert',$_REQUEST)) ? intval($_REQUEST['expert']) : 0);
if($_REQUEST['drophub']) {
require_once('hubloc.php');
hubloc_mark_as_down($_REQUEST['drophub']);
remove_queue_by_posturl($_REQUEST['drophub']);
}

View File

@@ -16,13 +16,7 @@ class Security {
$block_public = ((x($_POST,'block_public')) ? True : False);
set_config('system','block_public',$block_public);
$cloud_noroot = ((x($_POST,'cloud_noroot')) ? 1 : 0);
set_config('system','cloud_disable_siteroot',1 - $cloud_noroot);
$cloud_disksize = ((x($_POST,'cloud_disksize')) ? 1 : 0);
set_config('system','cloud_report_disksize',$cloud_disksize);
$ws = $this->trim_array_elems(explode("\n",$_POST['whitelisted_sites']));
set_config('system','whitelisted_sites',$ws);
@@ -58,24 +52,24 @@ class Security {
function get() {
$whitesites = get_config('system','whitelisted_sites');
$whitesites_str = ((is_array($whitesites)) ? implode("\n",$whitesites) : '');
$whitesites_str = ((is_array($whitesites)) ? implode($whitesites,"\n") : '');
$blacksites = get_config('system','blacklisted_sites');
$blacksites_str = ((is_array($blacksites)) ? implode("\n",$blacksites) : '');
$blacksites_str = ((is_array($blacksites)) ? implode($blacksites,"\n") : '');
$whitechannels = get_config('system','whitelisted_channels');
$whitechannels_str = ((is_array($whitechannels)) ? implode("\n",$whitechannels) : '');
$whitechannels_str = ((is_array($whitechannels)) ? implode($whitechannels,"\n") : '');
$blackchannels = get_config('system','blacklisted_channels');
$blackchannels_str = ((is_array($blackchannels)) ? implode("\n",$blackchannels) : '');
$blackchannels_str = ((is_array($blackchannels)) ? implode($blackchannels,"\n") : '');
$whiteembeds = get_config('system','embed_allow');
$whiteembeds_str = ((is_array($whiteembeds)) ? implode("\n",$whiteembeds) : '');
$whiteembeds_str = ((is_array($whiteembeds)) ? implode($whiteembeds,"\n") : '');
$blackembeds = get_config('system','embed_deny');
$blackembeds_str = ((is_array($blackembeds)) ? implode("\n",$blackembeds) : '');
$blackembeds_str = ((is_array($blackembeds)) ? implode($blackembeds,"\n") : '');
$embed_coop = intval(get_config('system','embed_coop'));
@@ -93,8 +87,6 @@ class Security {
'$page' => t('Security'),
'$form_security_token' => get_form_security_token('admin_security'),
'$block_public' => array('block_public', t("Block public"), get_config('system','block_public'), t("Check to block public access to all otherwise public personal pages on this site unless you are currently authenticated.")),
'$cloud_noroot' => [ 'cloud_noroot', t('Provide a cloud root directory'), 1 - intval(get_config('system','cloud_disable_siteroot')), t('The cloud root directory lists all channel names which provide public files') ],
'$cloud_disksize' => [ 'cloud_disksize', t('Show total disk space available to cloud uploads'), intval(get_config('system','cloud_report_disksize')), '' ],
'$transport_security' => array('transport_security', t('Set "Transport Security" HTTP header'),intval(get_config('system','transport_security_header')),''),
'$content_security' => array('content_security', t('Set "Content Security Policy" HTTP header'),intval(get_config('system','content_security_policy')),''),
'$allowed_email' => array('allowed_email', t("Allowed email domains"), get_config('system','allowed_email'), t("Comma separated list of domains which are allowed in email addresses for registrations to this site. Wildcards are accepted. Empty to allow any domains")),

View File

@@ -5,9 +5,11 @@ namespace Zotlabs\Module\Admin;
class Site {
/**
* @brief POST handler for Admin Site Page.
*
* @param App &$a
*/
function post(){
if (!x($_POST, 'page_site')) {
@@ -15,53 +17,38 @@ class Site {
}
check_form_security_token_redirectOnErr('/admin/site', 'admin_site');
$sitename = ((x($_POST,'sitename')) ? notags(trim($_POST['sitename'])) : '');
$server_role = ((x($_POST,'server_role')) ? notags(trim($_POST['server_role'])) : 'standard');
$banner = ((x($_POST,'banner')) ? trim($_POST['banner']) : false);
$banner = ((x($_POST,'banner')) ? trim($_POST['banner']) : false);
$admininfo = ((x($_POST,'admininfo')) ? trim($_POST['admininfo']) : false);
$siteinfo = ((x($_POST,'siteinfo')) ? trim($_POST['siteinfo']) : '');
$language = ((x($_POST,'language')) ? notags(trim($_POST['language'])) : '');
$theme = ((x($_POST,'theme')) ? notags(trim($_POST['theme'])) : '');
// $theme_mobile = ((x($_POST,'theme_mobile')) ? notags(trim($_POST['theme_mobile'])) : '');
// $site_channel = ((x($_POST,'site_channel')) ? notags(trim($_POST['site_channel'])) : '');
$theme_mobile = ((x($_POST,'theme_mobile')) ? notags(trim($_POST['theme_mobile'])) : '');
// $site_channel = ((x($_POST,'site_channel')) ? notags(trim($_POST['site_channel'])) : '');
$maximagesize = ((x($_POST,'maximagesize')) ? intval(trim($_POST['maximagesize'])) : 0);
$register_policy = ((x($_POST,'register_policy')) ? intval(trim($_POST['register_policy'])) : 0);
$minimum_age = ((x($_POST,'minimum_age')) ? intval(trim($_POST['minimum_age'])) : 13);
$access_policy = ((x($_POST,'access_policy')) ? intval(trim($_POST['access_policy'])) : 0);
$invite_only = ((x($_POST,'invite_only')) ? True : False);
$abandon_days = ((x($_POST,'abandon_days')) ? intval(trim($_POST['abandon_days'])) : 0);
$invite_only = ((x($_POST,'invite_only')) ? True : False);
$abandon_days = ((x($_POST,'abandon_days')) ? intval(trim($_POST['abandon_days'])) : 0);
$register_text = ((x($_POST,'register_text')) ? notags(trim($_POST['register_text'])) : '');
$site_sellpage = ((x($_POST,'site_sellpage')) ? notags(trim($_POST['site_sellpage'])) : '');
$site_location = ((x($_POST,'site_location')) ? notags(trim($_POST['site_location'])) : '');
$frontpage = ((x($_POST,'frontpage')) ? notags(trim($_POST['frontpage'])) : '');
$firstpage = ((x($_POST,'firstpage')) ? notags(trim($_POST['firstpage'])) : 'profiles');
$first_page = ((x($_POST,'first_page')) ? notags(trim($_POST['first_page'])) : 'profiles');
// check value after trim
if(! $first_page) {
$first_page = 'profiles';
}
$mirror_frontpage = ((x($_POST,'mirror_frontpage')) ? intval(trim($_POST['mirror_frontpage'])) : 0);
$directory_server = ((x($_POST,'directory_server')) ? trim($_POST['directory_server']) : '');
$allowed_sites = ((x($_POST,'allowed_sites')) ? notags(trim($_POST['allowed_sites'])) : '');
$force_publish = ((x($_POST,'publish_all')) ? True : False);
$disable_discover_tab = ((x($_POST,'disable_discover_tab')) ? False : True);
$site_firehose = ((x($_POST,'site_firehose')) ? True : False);
$open_pubstream = ((x($_POST,'open_pubstream')) ? True : False);
$login_on_homepage = ((x($_POST,'login_on_homepage')) ? True : False);
$enable_context_help = ((x($_POST,'enable_context_help')) ? True : False);
$frontpage = ((x($_POST,'frontpage')) ? notags(trim($_POST['frontpage'])) : '');
$mirror_frontpage = ((x($_POST,'mirror_frontpage')) ? intval(trim($_POST['mirror_frontpage'])) : 0);
$directory_server = ((x($_POST,'directory_server')) ? trim($_POST['directory_server']) : '');
$allowed_sites = ((x($_POST,'allowed_sites')) ? notags(trim($_POST['allowed_sites'])) : '');
$force_publish = ((x($_POST,'publish_all')) ? True : False);
$disable_discover_tab = ((x($_POST,'disable_discover_tab')) ? False : True);
$login_on_homepage = ((x($_POST,'login_on_homepage')) ? True : False);
$enable_context_help = ((x($_POST,'enable_context_help')) ? True : False);
$global_directory = ((x($_POST,'directory_submit_url')) ? notags(trim($_POST['directory_submit_url'])) : '');
$no_community_page = !((x($_POST,'no_community_page')) ? True : False);
$default_expire_days = ((array_key_exists('default_expire_days',$_POST)) ? intval($_POST['default_expire_days']) : 0);
$active_expire_days = ((array_key_exists('active_expire_days',$_POST)) ? intval($_POST['active_expire_days']) : 7);
$reply_address = ((array_key_exists('reply_address',$_POST) && trim($_POST['reply_address'])) ? trim($_POST['reply_address']) : 'noreply@' . \App::get_hostname());
$from_email = ((array_key_exists('from_email',$_POST) && trim($_POST['from_email'])) ? trim($_POST['from_email']) : 'Administrator@' . \App::get_hostname());
$from_email_name = ((array_key_exists('from_email_name',$_POST) && trim($_POST['from_email_name'])) ? trim($_POST['from_email_name']) : \Zotlabs\Lib\System::get_site_name());
$verifyssl = ((x($_POST,'verifyssl')) ? True : False);
$proxyuser = ((x($_POST,'proxyuser')) ? notags(trim($_POST['proxyuser'])) : '');
$proxy = ((x($_POST,'proxy')) ? notags(trim($_POST['proxy'])) : '');
@@ -72,60 +59,41 @@ class Site {
$maxloadavg = ((x($_POST,'maxloadavg')) ? intval(trim($_POST['maxloadavg'])) : 50);
$feed_contacts = ((x($_POST,'feed_contacts')) ? intval($_POST['feed_contacts']) : 0);
$verify_email = ((x($_POST,'verify_email')) ? 1 : 0);
$techlevel_lock = ((x($_POST,'techlock')) ? intval($_POST['techlock']) : 0);
$imagick_path = ((x($_POST,'imagick_path')) ? trim($_POST['imagick_path']) : '');
$thumbnail_security = ((x($_POST,'thumbnail_security')) ? intval($_POST['thumbnail_security']) : 0);
$force_queue = ((intval($_POST['force_queue']) > 0) ? intval($_POST['force_queue']) : 3000);
$pub_incl = escape_tags(trim($_POST['pub_incl']));
$pub_excl = escape_tags(trim($_POST['pub_excl']));
$permissions_role = escape_tags(trim($_POST['permissions_role']));
$techlevel_lock = ((x($_POST,'techlock')) ? intval($_POST['techlock']) : 0);
$techlevel = null;
if(array_key_exists('techlevel', $_POST))
if(array_key_exists('techlevel',$_POST))
$techlevel = intval($_POST['techlevel']);
set_config('system', 'server_role', $server_role);
set_config('system', 'feed_contacts', $feed_contacts);
set_config('system', 'delivery_interval', $delivery_interval);
set_config('system', 'delivery_batch_count', $delivery_batch_count);
set_config('system', 'poll_interval', $poll_interval);
set_config('system', 'maxloadavg', $maxloadavg);
set_config('system', 'frontpage', $frontpage);
set_config('system', 'sellpage', $site_sellpage);
set_config('system', 'workflow_channel_next', $first_page);
set_config('system', 'site_location', $site_location);
set_config('system', 'mirror_frontpage', $mirror_frontpage);
set_config('system', 'sitename', $sitename);
set_config('system', 'login_on_homepage', $login_on_homepage);
set_config('system', 'enable_context_help', $enable_context_help);
set_config('system', 'verify_email', $verify_email);
set_config('system', 'default_expire_days', $default_expire_days);
set_config('system', 'active_expire_days', $active_expire_days);
set_config('system', 'reply_address', $reply_address);
set_config('system', 'from_email', $from_email);
set_config('system', 'from_email_name' , $from_email_name);
set_config('system', 'imagick_convert_path' , $imagick_path);
set_config('system', 'thumbnail_security' , $thumbnail_security);
set_config('system', 'default_permissions_role', $permissions_role);
set_config('system', 'pubstream_incl',$pub_incl);
set_config('system', 'pubstream_excl',$pub_excl);
set_config('system', 'techlevel_lock', $techlevel_lock);
if(! is_null($techlevel))
set_config('system', 'techlevel', $techlevel);
if($directory_server)
set_config('system','directory_server',$directory_server);
if ($banner == '') {
del_config('system', 'banner');
} else {
set_config('system', 'banner', $banner);
}
if ($admininfo == ''){
del_config('system', 'admininfo');
} else {
@@ -133,42 +101,37 @@ class Site {
linkify_tags($a, $admininfo, local_channel());
set_config('system', 'admininfo', $admininfo);
}
set_config('system','siteinfo',$siteinfo);
set_config('system', 'language', $language);
set_config('system', 'theme', $theme);
// if ( $theme_mobile === '---' ) {
// del_config('system', 'mobile_theme');
// } else {
// set_config('system', 'mobile_theme', $theme_mobile);
// }
if ( $theme_mobile === '---' ) {
del_config('system', 'mobile_theme');
} else {
set_config('system', 'mobile_theme', $theme_mobile);
}
// set_config('system','site_channel', $site_channel);
set_config('system','maximagesize', $maximagesize);
set_config('system','register_policy', $register_policy);
set_config('system','minimum_age', $minimum_age);
set_config('system','invitation_only', $invite_only);
set_config('system','invitation_only', $invite_only);
set_config('system','access_policy', $access_policy);
set_config('system','account_abandon_days', $abandon_days);
set_config('system','register_text', $register_text);
set_config('system','allowed_sites', $allowed_sites);
set_config('system','publish_all', $force_publish);
set_config('system','disable_discover_tab', $disable_discover_tab);
set_config('system','site_firehose', $site_firehose);
set_config('system','open_pubstream', $open_pubstream);
set_config('system','force_queue_threshold', $force_queue);
if ($global_directory == '') {
del_config('system', 'directory_submit_url');
} else {
set_config('system', 'directory_submit_url', $global_directory);
}
set_config('system','no_community_page', $no_community_page);
set_config('system','no_utf', $no_utf);
set_config('system','verifyssl', $verifyssl);
set_config('system','proxyuser', $proxyuser);
set_config('system','proxy', $proxy);
set_config('system','curl_timeout', $timeout);
info( t('Site settings updated.') . EOL);
goaway(z_root() . '/admin/site' );
}
@@ -176,14 +139,15 @@ class Site {
/**
* @brief Admin page site.
*
* @return string with HTML
* @return string
*/
function get() {
function get() {
/* Installed langs */
$lang_choices = array();
$langs = glob('view/*/hstrings.php');
if(is_array($langs) && count($langs)) {
if(! in_array('view/en/hstrings.php',$langs))
$langs[] = 'view/en/';
@@ -193,7 +157,7 @@ class Site {
$lang_choices[$t[1]] = $t[1];
}
}
/* Installed themes */
$theme_choices_mobile["---"] = t("Default");
$theme_choices = array();
@@ -202,14 +166,6 @@ class Site {
foreach($files as $file) {
$vars = '';
$f = basename($file);
$info = get_theme_info($f);
$compatible = check_plugin_versions($info);
if(!$compatible) {
$theme_choices[$f] = $theme_choices_mobile[$f] = sprintf(t('%s - (Incompatible)'), $f);
continue;
}
if (file_exists($file . '/library'))
continue;
if (file_exists($file . '/mobile'))
@@ -228,16 +184,15 @@ class Site {
}
}
}
$dir_choices = null;
$dirmode = get_config('system','directory_mode');
$realm = get_directory_realm();
// directory server should not be set or settable unless we are a directory client
// avoid older redmatrix servers which don't have modern encryption
if($dirmode == DIRECTORY_MODE_NORMAL) {
$x = q("select site_url from site where site_flags in (%d,%d) and site_realm = '%s' and site_dead = 0 and site_project != 'redmatrix'",
$x = q("select site_url from site where site_flags in (%d,%d) and site_realm = '%s'",
intval(DIRECTORY_MODE_SECONDARY),
intval(DIRECTORY_MODE_PRIMARY),
dbesc($realm)
@@ -249,25 +204,25 @@ class Site {
}
}
}
/* Banner */
$banner = get_config('system', 'banner');
if($banner === false)
if($banner === false)
$banner = get_config('system','sitename');
$banner = htmlspecialchars($banner);
/* Admin Info */
$admininfo = get_config('system', 'admininfo');
/* Register policy */
$register_choices = Array(
REGISTER_CLOSED => t("No"),
REGISTER_APPROVE => t("Yes - with approval"),
REGISTER_OPEN => t("Yes")
);
/* Acess policy */
$access_choices = Array(
ACCESS_PRIVATE => t("My site is not a public server"),
@@ -275,33 +230,36 @@ class Site {
ACCESS_FREE => t("My site has free access only"),
ACCESS_TIERED => t("My site offers free accounts with optional paid upgrades")
);
$discover_tab = get_config('system','disable_discover_tab');
// $disable public streams by default
if($discover_tab === false)
$discover_tab = 1;
// now invert the logic for the setting.
$discover_tab = (1 - $discover_tab);
$server_roles = [
'basic' => t('Basic/Minimal Social Networking'),
'standard' => t('Standard Configuration (default)'),
'pro' => t('Professional')
];
$techlevels = [
'0' => t('Beginner/Basic'),
'1' => t('Novice - not skilled but willing to learn'),
'2' => t('Intermediate - somewhat comfortable'),
'3' => t('Advanced - very comfortable'),
'4' => t('Expert - I can write computer code'),
'4' => t('Expert - I can write computer code'),
'5' => t('Wizard - I probably know more than you do')
];
$perm_roles = \Zotlabs\Access\PermissionRoles::roles();
$default_role = get_config('system','default_permissions_role','social');
$role = array('permissions_role' , t('Default permission role for new accounts'), $default_role, t('This role will be used for the first channel created after registration.'),$perm_roles);
$homelogin = get_config('system','login_on_homepage');
$enable_context_help = get_config('system','enable_context_help');
$t = get_markup_template("admin_site.tpl");
return replace_macros($t, array(
'$title' => t('Administration'),
@@ -311,30 +269,30 @@ class Site {
'$upload' => t('File upload'),
'$corporate' => t('Policies'),
'$advanced' => t('Advanced'),
'$baseurl' => z_root(),
// name, label, value, help string, extra data...
'$sitename' => array('sitename', t("Site name"), htmlspecialchars(get_config('system','sitename'), ENT_QUOTES, 'UTF-8'),''),
'$server_role' => array('server_role', t("Server Configuration/Role"), get_config('system','server_role'),'',$server_roles),
'$techlevel' => [ 'techlevel', t('Site default technical skill level'), get_config('system','techlevel'), t('Used to provide a member experience matched to technical comfort level'), $techlevels ],
'$techlock' => [ 'techlock', t('Lock the technical skill level setting'), get_config('system','techlevel_lock'), t('Members can set their own technical comfort level by default') ],
'$banner' => array('banner', t("Banner/Logo"), $banner, t('Unfiltered HTML/CSS/JS is allowed')),
'$banner' => array('banner', t("Banner/Logo"), $banner, ""),
'$admininfo' => array('admininfo', t("Administrator Information"), $admininfo, t("Contact information for site administrators. Displayed on siteinfo page. BBCode can be used here")),
'$siteinfo' => array('siteinfo', t('Site Information'), get_config('system','siteinfo'), t("Publicly visible description of this site. Displayed on siteinfo page. BBCode can be used here")),
'$language' => array('language', t("System language"), get_config('system','language'), "", $lang_choices),
'$theme' => array('theme', t("System theme"), get_config('system','theme'), t("Default system theme - may be over-ridden by user profiles - <a href='#' id='cnftheme'>change theme settings</a>"), $theme_choices),
// '$theme_mobile' => array('theme_mobile', t("Mobile system theme"), get_config('system','mobile_theme'), t("Theme for mobile devices"), $theme_choices_mobile),
// '$site_channel' => array('site_channel', t("Channel to use for this website's static pages"), get_config('system','site_channel'), t("Site Channel")),
'$feed_contacts' => array('feed_contacts', t('Allow Feeds as Connections'),get_config('system','feed_contacts'),t('(Heavy system resource usage)')),
'$theme_mobile' => array('theme_mobile', t("Mobile system theme"), get_config('system','mobile_theme'), t("Theme for mobile devices"), $theme_choices_mobile),
// '$site_channel' => array('site_channel', t("Channel to use for this website's static pages"), get_config('system','site_channel'), t("Site Channel")),
'$feed_contacts' => array('feed_contacts', t('Allow Feeds as Connections'),get_config('system','feed_contacts'),t('(Heavy system resource usage)')),
'$maximagesize' => array('maximagesize', t("Maximum image size"), intval(get_config('system','maximagesize')), t("Maximum size in bytes of uploaded images. Default is 0, which means no limits.")),
'$register_policy' => array('register_policy', t("Does this site allow new member registration?"), get_config('system','register_policy'), "", $register_choices),
'$invite_only' => array('invite_only', t("Invitation only"), get_config('system','invitation_only'), t("Only allow new member registrations with an invitation code. Above register policy must be set to Yes.")),
'$minimum_age' => array('minimum_age', t("Minimum age"), (x(get_config('system','minimum_age'))?get_config('system','minimum_age'):13), t("Minimum age (in years) for who may register on this site.")),
'$access_policy' => array('access_policy', t("Which best describes the types of account offered by this hub?"), get_config('system','access_policy'), "This is displayed on the public server site list.", $access_choices),
'$register_text' => array('register_text', t("Register text"), htmlspecialchars(get_config('system','register_text'), ENT_QUOTES, 'UTF-8'), t("Will be displayed prominently on the registration page.")),
'$role' => $role,
'$frontpage' => array('frontpage', t("Site homepage to show visitors (default: login box)"), get_config('system','frontpage'), t("example: 'public' to show public stream, 'page/sys/home' to show a system webpage called 'home' or 'include:home.html' to include a file.")),
'$mirror_frontpage' => array('mirror_frontpage', t("Preserve site homepage URL"), get_config('system','mirror_frontpage'), t('Present the site homepage in a frame at the original location instead of redirecting')),
'$abandon_days' => array('abandon_days', t('Accounts abandoned after x days'), get_config('system','account_abandon_days'), t('Will not waste system resources polling external sites for abandonded accounts. Enter 0 for no time limit.')),
@@ -342,42 +300,24 @@ class Site {
'$verify_email' => array('verify_email', t("Verify Email Addresses"), get_config('system','verify_email'), t("Check to verify email addresses used in account registration (recommended).")),
'$force_publish' => array('publish_all', t("Force publish"), get_config('system','publish_all'), t("Check to force all profiles on this site to be listed in the site directory.")),
'$disable_discover_tab' => array('disable_discover_tab', t('Import Public Streams'), $discover_tab, t('Import and allow access to public content pulled from other sites. Warning: this content is unmoderated.')),
'$site_firehose' => array('site_firehose', t('Site only Public Streams'), get_config('system','site_firehose'), t('Allow access to public content originating only from this site if Imported Public Streams are disabled.')),
'$open_pubstream' => array('open_pubstream', t('Allow anybody on the internet to access the Public streams'), get_config('system','open_pubstream',1), t('Disable to require authentication before viewing. Warning: this content is unmoderated.')),
'$incl' => array('pub_incl',t('Only import Public stream posts with this text'), get_config('system','pubstream_incl'),t('words one per line or #tags or /patterns/ or lang=xx, leave blank to import all posts')),
'$excl' => array('pub_excl',t('Do not import Public stream posts with this text'), get_config('system','pubstream_excl'),t('words one per line or #tags or /patterns/ or lang=xx, leave blank to import all posts')),
'$login_on_homepage' => array('login_on_homepage', t("Login on Homepage"),((intval($homelogin) || $homelogin === false) ? 1 : '') , t("Present a login box to visitors on the home page if no other content has been configured.")),
'$enable_context_help' => array('enable_context_help', t("Enable context help"),((intval($enable_context_help) === 1 || $enable_context_help === false) ? 1 : 0) , t("Display contextual help for the current page when the help button is pressed.")),
'$reply_address' => [ 'reply_address', t('Reply-to email address for system generated email.'), get_config('system','reply_address','noreply@' . \App::get_hostname()),'' ],
'$from_email' => [ 'from_email', t('Sender (From) email address for system generated email.'), get_config('system','from_email','Administrator@' . \App::get_hostname()),'' ],
'$from_email_name' => [ 'from_email_name', t('Name of email sender for system generated email.'), get_config('system','from_email_name',\Zotlabs\Lib\System::get_site_name()),'' ],
'$directory_server' => (($dir_choices) ? array('directory_server', t("Directory Server URL"), get_config('system','directory_server'), t("Default directory server"), $dir_choices) : null),
'$proxyuser' => array('proxyuser', t("Proxy user"), get_config('system','proxyuser'), ""),
'$proxy' => array('proxy', t("Proxy URL"), get_config('system','proxy'), ""),
'$timeout' => array('timeout', t("Network timeout"), (x(get_config('system','curl_timeout'))?get_config('system','curl_timeout'):60), t("Value is in seconds. Set to 0 for unlimited (not recommended).")),
'$delivery_interval' => array('delivery_interval', t("Delivery interval"), (x(get_config('system','delivery_interval'))?get_config('system','delivery_interval'):2), t("Delay background delivery processes by this many seconds to reduce system load. Recommend: 4-5 for shared hosts, 2-3 for virtual private servers. 0-1 for large dedicated servers.")),
'$delivery_batch_count' => array('delivery_batch_count', t('Deliveries per process'),(x(get_config('system','delivery_batch_count'))?get_config('system','delivery_batch_count'):1), t("Number of deliveries to attempt in a single operating system process. Adjust if necessary to tune system performance. Recommend: 1-5.")),
'$force_queue' => array('force_queue', t("Queue Threshold"), get_config('system','force_queue_threshold',3000), t("Always defer immediate delivery if queue contains more than this number of entries.")),
'$poll_interval' => array('poll_interval', t("Poll interval"), (x(get_config('system','poll_interval'))?get_config('system','poll_interval'):2), t("Delay background polling processes by this many seconds to reduce system load. If 0, use delivery interval.")),
'$imagick_path' => array('imagick_path', t("Path to ImageMagick convert program"), get_config('system','imagick_convert_path'), t("If set, use this program to generate photo thumbnails for huge images ( > 4000 pixels in either dimension), otherwise memory exhaustion may occur. Example: /usr/bin/convert")),
'$thumbnail_security' => array('thumbnail_security', t("Allow SVG thumbnails in file browser"), get_config('system','thumbnail_security',0), t("WARNING: SVG images may contain malicious code.")),
'$maxloadavg' => array('maxloadavg', t("Maximum Load Average"), ((intval(get_config('system','maxloadavg')) > 0)?get_config('system','maxloadavg'):50), t("Maximum system load before delivery and poll processes are deferred - default 50.")),
'$default_expire_days' => array('default_expire_days', t('Expiration period in days for imported (grid/network) content'), intval(get_config('system','default_expire_days')), t('0 for no expiration of imported content')),
'$active_expire_days' => array('active_expire_days', t('Do not expire any posts which have comments less than this many days ago'), intval(get_config('system','active_expire_days',7)), ''),
'$sellpage' => array('site_sellpage', t('Public servers: Optional landing (marketing) webpage for new registrants'), get_config('system','sellpage',''), sprintf( t('Create this page first. Default is %s/register'),z_root())),
'$first_page' => array('first_page', t('Page to display after creating a new channel'), get_config('system','workflow_channel_next','profiles'), t('Default: profiles')),
'$location' => array('site_location', t('Optional: site location'), get_config('system','site_location',''), t('Region or country')),
'$form_security_token' => get_form_security_token("admin_site"),
));
}
}
}

View File

@@ -2,41 +2,38 @@
namespace Zotlabs\Module\Admin;
use \Michelf\MarkdownExtra;
/**
* @brief Admin area theme settings.
*/
class Themes {
/**
* @brief
*
*/
function post() {
$theme = argv(2);
if (is_file("view/theme/$theme/php/config.php")){
require_once("view/theme/$theme/php/config.php");
/// @FIXME add parent theme if derived
if (function_exists('theme_admin_post')){
// fixme add parent theme if derived
if (function_exists("theme_admin_post")){
theme_admin_post($a);
}
}
info(t('Theme settings updated.'));
if(is_ajax())
if(is_ajax())
return;
goaway(z_root() . '/admin/themes/' . $theme );
}
/**
* @brief Themes admin page.
*
* @return string with parsed HTML
* @return string
*/
function get(){
$allowed_themes_str = get_config('system', 'allowed_themes');
$allowed_themes_raw = explode(',', $allowed_themes_str);
$allowed_themes = array();
@@ -44,7 +41,7 @@ class Themes {
foreach($allowed_themes_raw as $x)
if(strlen(trim($x)))
$allowed_themes[] = trim($x);
$themes = array();
$files = glob('view/theme/*');
if($files) {
@@ -56,55 +53,56 @@ class Themes {
$themes[] = array('name' => $f, 'experimental' => $is_experimental, 'supported' => $is_supported, 'allowed' => $is_allowed);
}
}
if(! count($themes)) {
notice( t('No themes found.'));
return '';
}
/*
* Single theme
*/
if (\App::$argc == 3){
$theme = \App::$argv[2];
if(! is_dir("view/theme/$theme")){
notice( t("Item not found.") );
return '';
}
if (x($_GET,"a") && $_GET['a']=="t"){
check_form_security_token_redirectOnErr('/admin/themes', 'admin_themes', 't');
// Toggle theme status
$this->toggle_theme($themes, $theme, $result);
$s = $this->rebuild_theme_table($themes);
if($result)
info( sprintf('Theme %s enabled.', $theme));
else
info( sprintf('Theme %s disabled.', $theme));
set_config('system', 'allowed_themes', $s);
goaway(z_root() . '/admin/themes' );
}
// display theme details
require_once('library/markdown.php');
if ($this->theme_status($themes,$theme)) {
$status="on"; $action= t("Disable");
} else {
$status="off"; $action= t("Enable");
}
$readme=Null;
if (is_file("view/theme/$theme/README.md")){
$readme = file_get_contents("view/theme/$theme/README.md");
$readme = MarkdownExtra::defaultTransform($readme);
$readme = Markdown($readme);
} else if (is_file("view/theme/$theme/README")){
$readme = '<pre>'. file_get_contents("view/theme/$theme/README") .'</pre>';
$readme = "<pre>". file_get_contents("view/theme/$theme/README") ."</pre>";
}
$admin_form = '';
if (is_file("view/theme/$theme/php/config.php")){
require_once("view/theme/$theme/php/config.php");
@@ -112,11 +110,11 @@ class Themes {
$admin_form = theme_admin($a);
}
}
$screenshot = array( get_theme_screenshot($theme), t('Screenshot'));
if(! stristr($screenshot[0],$theme))
$screenshot = null;
$t = get_markup_template('admin_plugins_details.tpl');
return replace_macros($t, array(
'$title' => t('Administration'),
@@ -124,7 +122,7 @@ class Themes {
'$toggle' => t('Toggle'),
'$settings' => t('Settings'),
'$baseurl' => z_root(),
'$plugin' => $theme,
'$status' => $status,
'$action' => $action,
@@ -135,22 +133,22 @@ class Themes {
'$str_maintainer' => t('Maintainer: '),
'$screenshot' => $screenshot,
'$readme' => $readme,
'$form_security_token' => get_form_security_token('admin_themes'),
));
}
/*
* List themes
*/
$xthemes = array();
if($themes) {
foreach($themes as $th) {
$xthemes[] = array($th['name'],(($th['allowed']) ? "on" : "off"), get_theme_info($th['name']));
}
}
$t = get_markup_template('admin_plugins.tpl');
return replace_macros($t, array(
'$title' => t('Administration'),
@@ -164,14 +162,13 @@ class Themes {
'$form_security_token' => get_form_security_token('admin_themes'),
));
}
/**
* @brief Toggle a theme.
*
* @param array &$themes
* @param[in] string $th
* @param[out] int &$result
* @param array $themes
* @param string $th
* @param int $result
*/
function toggle_theme(&$themes, $th, &$result) {
for($x = 0; $x < count($themes); $x ++) {
@@ -187,7 +184,7 @@ class Themes {
}
}
}
/**
* @param array $themes
* @param string $th
@@ -206,7 +203,8 @@ class Themes {
}
return 0;
}
/**
* @param array $themes
* @return string
@@ -224,5 +222,12 @@ class Themes {
}
return $o;
}
}
}

View File

@@ -3,22 +3,10 @@ namespace Zotlabs\Module;
require_once('include/api.php');
class Api extends \Zotlabs\Web\Controller {
function init() {
zot_api_init();
api_register_func('api/client/register', 'api_client_register', false);
api_register_func('api/oauth/request_token', 'api_oauth_request_token', false);
api_register_func('api/oauth/access_token', 'api_oauth_access_token', false);
$args = [];
call_hooks('api_register',$args);
return;
}
function post() {
if(! local_channel()) {
notice( t('Permission denied.') . EOL);
@@ -29,22 +17,20 @@ class Api extends \Zotlabs\Web\Controller {
function get() {
if(\App::$cmd === 'api/oauth/authorize'){
if(\App::$cmd=='api/oauth/authorize'){
/*
* api/oauth/authorize interact with the user. return a standard page
*/
\App::$page['template'] = 'minimal';
\App::$page['template'] = "minimal";
// get consumer/client from request token
try {
$request = \OAuth1Request::from_request();
$request = OAuth1Request::from_request();
}
catch(\Exception $e) {
logger('OAuth exception: ' . print_r($e,true));
// echo "<pre>"; var_dump($e);
killme();
echo "<pre>"; var_dump($e); killme();
}
@@ -54,10 +40,10 @@ class Api extends \Zotlabs\Web\Controller {
if (is_null($app))
return "Invalid request. Unknown token.";
$consumer = new \OAuth1Consumer($app['client_id'], $app['pw'], $app['redirect_uri']);
$consumer = new OAuth1Consumer($app['client_id'], $app['pw'], $app['redirect_uri']);
$verifier = md5($app['secret'] . local_channel());
set_config('oauth', $verifier, local_channel());
$verifier = md5($app['secret'].local_channel());
set_config("oauth", $verifier, local_channel());
if($consumer->callback_url != null) {
@@ -65,7 +51,7 @@ class Api extends \Zotlabs\Web\Controller {
$glue = '?';
if(strstr($consumer->callback_url,$glue))
$glue = '?';
goaway($consumer->callback_url . $glue . "oauth_token=" . \OAuth1Util::urlencode_rfc3986($params['oauth_token']) . "&oauth_verifier=" . \OAuth1Util::urlencode_rfc3986($verifier));
goaway($consumer->callback_url . $glue . "oauth_token=" . OAuth1Util::urlencode_rfc3986($params['oauth_token']) . "&oauth_verifier=" . OAuth1Util::urlencode_rfc3986($verifier));
killme();
}
@@ -92,11 +78,11 @@ class Api extends \Zotlabs\Web\Controller {
$tpl = get_markup_template('oauth_authorize.tpl');
$o = replace_macros($tpl, array(
'$title' => t('Authorize application connection'),
'$app' => $app,
'$title' => t('Authorize application connection'),
'$app' => $app,
'$authorize' => t('Do you want to authorize this application to access your posts and contacts, and/or create new posts for you?'),
'$yes' => t('Yes'),
'$no' => t('No'),
'$yes' => t('Yes'),
'$no' => t('No'),
));
//echo "<pre>"; var_dump($app); killme();

View File

@@ -25,10 +25,8 @@ class Appman extends \Zotlabs\Web\Controller {
'photo' => escape_tags($_REQUEST['photo']),
'version' => escape_tags($_REQUEST['version']),
'price' => escape_tags($_REQUEST['price']),
'page' => escape_tags($_REQUEST['page']),
'requires' => escape_tags($_REQUEST['requires']),
'system' => intval($_REQUEST['system']),
'plugin' => escape_tags($_REQUEST['plugin']),
'sig' => escape_tags($_REQUEST['sig']),
'categories' => escape_tags($_REQUEST['categories'])
);
@@ -37,9 +35,8 @@ class Appman extends \Zotlabs\Web\Controller {
if(Zlib\Apps::app_installed(local_channel(),$arr))
info( t('App installed.') . EOL);
goaway(z_root() . '/apps');
return; //not reached
return;
}
@@ -59,22 +56,13 @@ class Appman extends \Zotlabs\Web\Controller {
if($_POST['delete']) {
Zlib\Apps::app_destroy(local_channel(),$papp);
}
if($_POST['edit']) {
return;
}
if($_POST['feature']) {
Zlib\Apps::app_feature(local_channel(), $papp, $_POST['feature']);
}
if($_POST['pin']) {
Zlib\Apps::app_feature(local_channel(), $papp, $_POST['pin']);
}
if($_SESSION['return_url'])
goaway(z_root() . '/' . $_SESSION['return_url']);
goaway(z_root() . '/apps');
@@ -87,22 +75,8 @@ class Appman extends \Zotlabs\Web\Controller {
notice( t('Permission denied.') . EOL);
return;
}
$channel = \App::get_channel();
if(argc() > 2) {
if(argv(2) === 'moveup') {
Zlib\Apps::moveup(local_channel(),argv(1));
}
if(argv(2) === 'movedown') {
Zlib\Apps::movedown(local_channel(),argv(1));
}
goaway(z_root() . '/apporder');
}
$app = null;
$embed = null;
if($_REQUEST['appid']) {
@@ -147,7 +121,6 @@ class Appman extends \Zotlabs\Web\Controller {
'$price' => array('price', t('Price of app'),(($app) ? $app['app_price'] : ''), ''),
'$page' => array('page', t('Location (URL) to purchase app'),(($app) ? $app['app_page'] : ''), ''),
'$system' => (($app) ? intval($app['app_system']) : 0),
'$plugin' => (($app) ? $app['app_plugin'] : ''),
'$requires' => (($app) ? $app['app_requires'] : ''),
'$embed' => $embed,
'$submit' => t('Submit')

View File

@@ -1,51 +0,0 @@
<?php
namespace Zotlabs\Module;
use \Zotlabs\Lib as Zlib;
class Apporder extends \Zotlabs\Web\Controller {
function post() {
}
function get() {
if(! local_channel())
return;
nav_set_selected('Order Apps');
$syslist = array();
$list = Zlib\Apps::app_list(local_channel(), false, ['nav_featured_app', 'nav_pinned_app']);
if($list) {
foreach($list as $li) {
$syslist[] = Zlib\Apps::app_encode($li);
}
}
Zlib\Apps::translate_system_apps($syslist);
usort($syslist,'Zotlabs\\Lib\\Apps::app_name_compare');
$syslist = Zlib\Apps::app_order(local_channel(),$syslist);
foreach($syslist as $app) {
if(strpos($app['categories'],'nav_pinned_app') !== false) {
$navbar_apps[] = Zlib\Apps::app_render($app,'nav-order');
}
else {
$nav_apps[] = Zlib\Apps::app_render($app,'nav-order');
}
}
return replace_macros(get_markup_template('apporder.tpl'),
[
'$header' => [t('Change Order of Pinned Navbar Apps'), t('Change Order of App Tray Apps')],
'$desc' => [t('Use arrows to move the corresponding app left (top) or right (bottom) in the navbar'), t('Use arrows to move the corresponding app up or down in the app tray')],
'$nav_apps' => $nav_apps,
'$navbar_apps' => $navbar_apps
]
);
}
}

View File

@@ -7,25 +7,21 @@ use \Zotlabs\Lib as Zlib;
class Apps extends \Zotlabs\Web\Controller {
function get() {
nav_set_selected('Apps');
if(argc() == 2 && argv(1) == 'edit')
$mode = 'edit';
else
$mode = 'list';
$available = ((argc() == 2 && argv(1) === 'available') ? true : false);
$_SESSION['return_url'] = \App::$query_string;
$_SESSION['return_url'] = \App::$cmd;
$apps = array();
if(local_channel()) {
Zlib\Apps::import_system_apps();
$syslist = array();
$cat = ((array_key_exists('cat',$_GET) && $_GET['cat']) ? [ escape_tags($_GET['cat']) ] : '');
$list = Zlib\Apps::app_list((($available) ? 0 : local_channel()), (($mode == 'edit') ? true : false), $cat);
$list = Zlib\Apps::app_list(local_channel(), false, $_GET['cat']);
if($list) {
foreach($list as $x) {
$syslist[] = Zlib\Apps::app_encode($x);
@@ -41,17 +37,14 @@ class Apps extends \Zotlabs\Web\Controller {
// logger('apps: ' . print_r($syslist,true));
foreach($syslist as $app) {
$apps[] = Zlib\Apps::app_render($app,(($available) ? 'install' : $mode));
$apps[] = Zlib\Apps::app_render($app,$mode);
}
return replace_macros(get_markup_template('myapps.tpl'), array(
'$sitename' => get_config('system','sitename'),
'$cat' => $cat,
'$cat' => ((array_key_exists('cat',$_GET) && $_GET['cat']) ? ' - ' . escape_tags($_GET['cat']) : ''),
'$title' => t('Apps'),
'$apps' => $apps,
'$authed' => ((local_channel()) ? true : false),
'$manage' => (($available) ? '' : t('Manage apps')),
'$create' => (($mode == 'edit') ? t('Create new app') : '')
));
}

View File

@@ -1,139 +0,0 @@
<?php
namespace Zotlabs\Module;
require_once('include/channel.php');
require_once('include/acl_selectors.php');
require_once('include/conversation.php');
class Article_edit extends \Zotlabs\Web\Controller {
function get() {
// Figure out which post we're editing
$post_id = ((argc() > 1) ? intval(argv(1)) : 0);
if(! $post_id) {
notice( t('Item not found') . EOL);
return;
}
$itm = q("SELECT * FROM item WHERE id = %d and item_type = %d LIMIT 1",
intval($post_id),
intval(ITEM_TYPE_ARTICLE)
);
if($itm) {
$item_id = q("select * from iconfig where cat = 'system' and k = 'ARTICLE' and iid = %d limit 1",
intval($itm[0]['id'])
);
if($item_id)
$card_title = $item_id[0]['v'];
}
else {
notice( t('Item not found') . EOL);
return;
}
$owner = $itm[0]['uid'];
$uid = local_channel();
$observer = \App::get_observer();
$channel = channelx_by_n($owner);
if(! $channel) {
notice( t('Channel not found.') . EOL);
return;
}
$ob_hash = (($observer) ? $observer['xchan_hash'] : '');
if(! perm_is_allowed($owner,$ob_hash,'write_pages')) {
notice( t('Permission denied.') . EOL);
return;
}
$is_owner = (($uid && $uid == $owner) ? true : false);
$o = '';
$category = '';
$catsenabled = ((feature_enabled($owner,'categories')) ? 'categories' : '');
if ($catsenabled){
$itm = fetch_post_tags($itm);
$cats = get_terms_oftype($itm[0]['term'], TERM_CATEGORY);
foreach ($cats as $cat) {
if (strlen($category))
$category .= ', ';
$category .= $cat['term'];
}
}
if($itm[0]['attach']) {
$j = json_decode($itm[0]['attach'],true);
if($j) {
foreach($j as $jj) {
$itm[0]['body'] .= "\n" . '[attachment]' . basename($jj['href']) . ',' . $jj['revision'] . '[/attachment]' . "\n";
}
}
}
$mimetype = $itm[0]['mimetype'];
$content = $itm[0]['body'];
$rp = 'articles/' . $channel['channel_address'];
$x = array(
'nickname' => $channel['channel_address'],
'bbco_autocomplete'=> 'bbcode',
'return_path' => $rp,
'webpage' => ITEM_TYPE_ARTICLE,
'button' => t('Edit'),
'writefiles' => perm_is_allowed($owner, get_observer_hash(), 'write_pages'),
'weblink' => t('Insert web link'),
'hide_voting' => false,
'hide_future' => false,
'hide_location' => false,
'hide_expire' => false,
'showacl' => true,
'acl' => populate_acl($itm[0],false,\Zotlabs\Lib\PermissionDescription::fromGlobalPermission('view_pages')),
'permissions' => $itm[0],
'lockstate' => (($itm[0]['allow_cid'] || $itm[0]['allow_gid'] || $itm[0]['deny_cid'] || $itm[0]['deny_gid']) ? 'lock' : 'unlock'),
'ptyp' => $itm[0]['type'],
'mimeselect' => false,
'mimetype' => $itm[0]['mimetype'],
'body' => undo_post_tagging($content),
'post_id' => $post_id,
'visitor' => true,
'title' => htmlspecialchars($itm[0]['title'],ENT_COMPAT,'UTF-8'),
'placeholdertitle' => t('Title (optional)'),
'pagetitle' => $card_title,
'profile_uid' => (intval($channel['channel_id'])),
'catsenabled' => $catsenabled,
'category' => $category,
'bbcode' => (($mimetype == 'text/bbcode') ? true : false)
);
$editor = status_editor($a, $x);
$o .= replace_macros(get_markup_template('edpost_head.tpl'), array(
'$title' => t('Edit Article'),
'$delete' => ((($itm[0]['author_xchan'] === $ob_hash) || ($itm[0]['owner_xchan'] === $ob_hash)) ? t('Delete') : false),
'$id' => $itm[0]['id'],
'$cancel' => t('Cancel'),
'$editor' => $editor
));
return $o;
}
}

View File

@@ -1,200 +0,0 @@
<?php
namespace Zotlabs\Module;
require_once('include/channel.php');
require_once('include/conversation.php');
require_once('include/acl_selectors.php');
class Articles extends \Zotlabs\Web\Controller {
function init() {
if(argc() > 1)
$which = argv(1);
else
return;
profile_load($which);
}
function get($update = 0, $load = false) {
if(observer_prohibited(true)) {
return login();
}
if(! \App::$profile) {
notice( t('Requested profile is not available.') . EOL );
\App::$error = 404;
return;
}
if(! feature_enabled(\App::$profile_uid,'articles')) {
return;
}
nav_set_selected(t('Articles'));
head_add_link([
'rel' => 'alternate',
'type' => 'application/json+oembed',
'href' => z_root() . '/oep?f=&url=' . urlencode(z_root() . '/' . \App::$query_string),
'title' => 'oembed'
]);
$category = (($_REQUEST['cat']) ? escape_tags(trim($_REQUEST['cat'])) : '');
if($category) {
$sql_extra2 .= protect_sprintf(term_item_parent_query(\App::$profile['profile_uid'],'item', $category, TERM_CATEGORY));
}
$which = argv(1);
$selected_card = ((argc() > 2) ? argv(2) : '');
$_SESSION['return_url'] = \App::$query_string;
$uid = local_channel();
$owner = \App::$profile_uid;
$observer = \App::get_observer();
$ob_hash = (($observer) ? $observer['xchan_hash'] : '');
if(! perm_is_allowed($owner,$ob_hash,'view_pages')) {
notice( t('Permission denied.') . EOL);
return;
}
$is_owner = ($uid && $uid == $owner);
$channel = channelx_by_n($owner);
if($channel) {
$channel_acl = array(
'allow_cid' => $channel['channel_allow_cid'],
'allow_gid' => $channel['channel_allow_gid'],
'deny_cid' => $channel['channel_deny_cid'],
'deny_gid' => $channel['channel_deny_gid']
);
}
else {
$channel_acl = [ 'allow_cid' => '', 'allow_gid' => '', 'deny_cid' => '', 'deny_gid' => '' ];
}
if(perm_is_allowed($owner,$ob_hash,'write_pages')) {
$x = [
'webpage' => ITEM_TYPE_ARTICLE,
'is_owner' => true,
'content_label' => t('Add Article'),
'button' => t('Create'),
'nickname' => $channel['channel_address'],
'lockstate' => (($channel['channel_allow_cid'] || $channel['channel_allow_gid']
|| $channel['channel_deny_cid'] || $channel['channel_deny_gid']) ? 'lock' : 'unlock'),
'acl' => (($is_owner) ? populate_acl($channel_acl, false,
\Zotlabs\Lib\PermissionDescription::fromGlobalPermission('view_pages')) : ''),
'permissions' => $channel_acl,
'showacl' => (($is_owner) ? true : false),
'visitor' => true,
'body' => '[summary][/summary]',
'hide_location' => false,
'hide_voting' => false,
'profile_uid' => intval($owner),
'mimetype' => 'text/bbcode',
'mimeselect' => false,
'layoutselect' => false,
'expanded' => false,
'novoting' => false,
'catsenabled' => feature_enabled($owner,'categories'),
'bbco_autocomplete' => 'bbcode',
'bbcode' => true
];
if($_REQUEST['title'])
$x['title'] = $_REQUEST['title'];
if($_REQUEST['body'])
$x['body'] = $_REQUEST['body'];
$editor = status_editor($a,$x);
}
else {
$editor = '';
}
$itemspage = get_pconfig(local_channel(),'system','itemspage');
\App::set_pager_itemspage(((intval($itemspage)) ? $itemspage : 20));
$pager_sql = sprintf(" LIMIT %d OFFSET %d ", intval(\App::$pager['itemspage']), intval(\App::$pager['start']));
$sql_extra = item_permissions_sql($owner);
$sql_item = '';
if($selected_card) {
$r = q("select * from iconfig where iconfig.cat = 'system' and iconfig.k = 'ARTICLE' and iconfig.v = '%s' limit 1",
dbesc($selected_card)
);
if($r) {
$sql_item = "and item.id = " . intval($r[0]['iid']) . " ";
}
}
$r = q("select * from item
where item.uid = %d and item_type = %d
$sql_extra $sql_item order by item.created desc $pager_sql",
intval($owner),
intval(ITEM_TYPE_ARTICLE)
);
$item_normal = " and item.item_hidden = 0 and item.item_type in (0,7) and item.item_deleted = 0
and item.item_unpublished = 0 and item.item_delayed = 0 and item.item_pending_remove = 0
and item.item_blocked = 0 ";
if($r) {
$pager_total = count($r);
$parents_str = ids_to_querystr($r,'id');
$items = q("SELECT item.*, item.id AS item_id
FROM item
WHERE item.uid = %d $item_normal
AND item.parent IN ( %s )
$sql_extra $sql_extra2 ",
intval(\App::$profile['profile_uid']),
dbesc($parents_str)
);
if($items) {
xchan_query($items);
$items = fetch_post_tags($items, true);
$items = conv_sort($items,'updated');
}
else
$items = [];
}
$mode = 'articles';
if(get_pconfig(local_channel(),'system','articles_list_mode') && (! $selected_card))
$page_mode = 'pager_list';
else
$page_mode = 'traditional';
$content = conversation($items,$mode,false,$page_mode);
$o = replace_macros(get_markup_template('cards.tpl'), [
'$title' => t('Articles'),
'$editor' => $editor,
'$content' => $content,
'$pager' => alt_pager($pager_total)
]);
return $o;
}
}

View File

@@ -31,7 +31,7 @@ class Attach extends \Zotlabs\Web\Controller {
$unsafe_types = array('text/html','text/css','application/javascript');
if(in_array($r['data']['filetype'],$unsafe_types) && (! channel_codeallowed($r['data']['uid']))) {
if(in_array($r['data']['filetype'],$unsafe_types)) {
header('Content-type: text/plain');
}
else {

View File

@@ -1,96 +0,0 @@
<?php
namespace Zotlabs\Module;
use Zotlabs\Identity\OAuth2Storage;
class Authorize extends \Zotlabs\Web\Controller {
function get() {
if (!local_channel()) {
return login();
} else {
// TODO: Fully implement the dynamic client registration protocol:
// OpenID Connect Dynamic Client Registration 1.0 Client Metadata
// http://openid.net/specs/openid-connect-registration-1_0.html
$app = array(
'name' => (x($_REQUEST, 'client_name') ? urldecode($_REQUEST['client_name']) : t('Unknown App')),
'icon' => (x($_REQUEST, 'logo_uri') ? urldecode($_REQUEST['logo_uri']) : z_root() . '/images/icons/plugin.png'),
'url' => (x($_REQUEST, 'client_uri') ? urldecode($_REQUEST['client_uri']) : ''),
);
$o .= replace_macros(get_markup_template('oauth_authorize.tpl'), array(
'$title' => t('Authorize'),
'$authorize' => sprintf( t('Do you authorize the app %s to access your channel data?'), '<a style="float: none;" href="' . $app['url'] . '">' . $app['name'] . '</a> '),
'$app' => $app,
'$yes' => t('Allow'),
'$no' => t('Deny'),
'$client_id' => (x($_REQUEST, 'client_id') ? $_REQUEST['client_id'] : ''),
'$redirect_uri' => (x($_REQUEST, 'redirect_uri') ? $_REQUEST['redirect_uri'] : ''),
'$state' => (x($_REQUEST, 'state') ? $_REQUEST['state'] : ''),
));
return $o;
}
}
function post() {
if (! local_channel()) {
return;
}
$storage = new OAuth2Storage(\DBA::$dba->db);
$s = new \Zotlabs\Identity\OAuth2Server($storage);
// TODO: The automatic client registration protocol below should adhere more
// closely to "OAuth 2.0 Dynamic Client Registration Protocol" defined
// at https://tools.ietf.org/html/rfc7591
// If no client_id was provided, generate a new one.
if (x($_POST, 'client_id')) {
$client_id = $_POST['client_id'];
} else {
$client_id = $_POST['client_id'] = random_string(16);
}
// If no redirect_uri was provided, generate a fake one.
if (x($_POST, 'redirect_uri')) {
$redirect_uri = $_POST['redirect_uri'];
} else {
$redirect_uri = $_POST['redirect_uri'] = 'https://fake.example.com/oauth';
}
$request = \OAuth2\Request::createFromGlobals();
$response = new \OAuth2\Response();
// If the client is not registered, add to the database
if (!$client = $storage->getClientDetails($client_id)) {
$client_secret = random_string(16);
// Client apps are registered per channel
$user_id = local_channel();
$storage->setClientDetails($client_id, $client_secret, $redirect_uri, 'authorization_code', null, $user_id);
}
if (!$client = $storage->getClientDetails($client_id)) {
// There was an error registering the client.
$response->send();
killme();
}
$response->setParameter('client_secret', $client['client_secret']);
// validate the authorize request
if (!$s->validateAuthorizeRequest($request, $response)) {
$response->send();
killme();
}
// print the authorization code if the user has authorized your client
$is_authorized = ($_POST['authorize'] === 'allow');
$s->handleAuthorizeRequest($request, $response, $is_authorized, local_channel());
if ($is_authorized) {
$code = substr($response->getHttpHeader('Location'), strpos($response->getHttpHeader('Location'), 'code=') + 5, 40);
logger('Authorization Code: ' . $code);
}
$response->send();
killme();
}
}

View File

@@ -3,6 +3,8 @@ namespace Zotlabs\Module;
require_once('include/items.php');
require_once('include/conversation.php');
require_once('include/page_widgets.php');
class Block extends \Zotlabs\Web\Controller {

View File

@@ -7,9 +7,6 @@ class Bookmarks extends \Zotlabs\Web\Controller {
function init() {
if(! local_channel())
return;
nav_set_selected('View Bookmarks');
$item_id = intval($_REQUEST['item']);
$burl = trim($_REQUEST['burl']);
@@ -71,8 +68,7 @@ class Bookmarks extends \Zotlabs\Web\Controller {
$channel = \App::get_channel();
//$o = profile_tabs($a,true,$channel['channel_address']);
$o = '';
$o = profile_tabs($a,true,$channel['channel_address']);
$o .= '<div class="generic-content-wrapper-styled">';

View File

@@ -69,8 +69,6 @@ class Cal extends \Zotlabs\Web\Controller {
notice( t('Permissions denied.') . EOL);
return;
}
nav_set_selected('Calendar');
$sql_extra = permissions_sql($channel['channel_id'],get_observer_hash(),'event');
@@ -88,8 +86,7 @@ class Cal extends \Zotlabs\Web\Controller {
$o = '';
//$tabs = profile_tabs($a, True, $channel['channel_address']);
$tabs = '';
$tabs = profile_tabs($a, True, $channel['channel_address']);
$mode = 'view';
$y = 0;
@@ -112,7 +109,7 @@ class Cal extends \Zotlabs\Web\Controller {
/* edit/create form */
if($event_id) {
$r = q("SELECT * FROM event WHERE event_hash = '%s' AND uid = %d LIMIT 1",
$r = q("SELECT * FROM `event` WHERE event_hash = '%s' AND `uid` = %d LIMIT 1",
dbesc($event_id),
intval($channel['channel_id'])
);
@@ -212,10 +209,6 @@ class Cal extends \Zotlabs\Web\Controller {
$adjust_start = datetime_convert('UTC', date_default_timezone_get(), $start);
$adjust_finish = datetime_convert('UTC', date_default_timezone_get(), $finish);
if(! perm_is_allowed(\App::$profile['uid'],get_observer_hash(),'view_contacts'))
$sql_extra .= " and etype != 'birthday' ";
if (x($_GET,'id')){
$r = q("SELECT event.*, item.plink, item.item_flags, item.author_xchan, item.owner_xchan
from event left join item on resource_id = event_hash where resource_type = 'event' and event.uid = %d and event.id = %d $sql_extra limit 1",
@@ -231,7 +224,7 @@ class Cal extends \Zotlabs\Web\Controller {
$r = q("SELECT event.*, item.plink, item.item_flags, item.author_xchan, item.owner_xchan
from event left join item on event_hash = resource_id
where resource_type = 'event' and event.uid = %d and event.uid = item.uid $ignored
where resource_type = 'event' and event.uid = %d $ignored
AND (( adjust = 0 AND ( dtend >= '%s' or nofinish = 1 ) AND dtstart <= '%s' )
OR ( adjust = 1 AND ( dtend >= '%s' or nofinish = 1 ) AND dtstart <= '%s' )) $sql_extra ",
intval($channel['channel_id']),
@@ -295,8 +288,8 @@ class Cal extends \Zotlabs\Web\Controller {
$title = strip_tags(html_entity_decode($title,ENT_QUOTES,'UTF-8'));
}
$html = format_event_html($rr);
$rr['desc'] = zidify_links(smilies(bbcode($rr['desc'])));
$rr['location'] = zidify_links(smilies(bbcode($rr['location'])));
$rr['desc'] = bbcode($rr['desc']);
$rr['location'] = bbcode($rr['location']);
$events[] = array(
'id'=>$rr['id'],
'hash' => $rr['event_hash'],

View File

@@ -1,139 +0,0 @@
<?php
namespace Zotlabs\Module;
require_once('include/channel.php');
require_once('include/acl_selectors.php');
require_once('include/conversation.php');
class Card_edit extends \Zotlabs\Web\Controller {
function get() {
// Figure out which post we're editing
$post_id = ((argc() > 1) ? intval(argv(1)) : 0);
if(! $post_id) {
notice( t('Item not found') . EOL);
return;
}
$itm = q("SELECT * FROM item WHERE id = %d and item_type = %d LIMIT 1",
intval($post_id),
intval(ITEM_TYPE_CARD)
);
if($itm) {
$item_id = q("select * from iconfig where cat = 'system' and k = 'CARD' and iid = %d limit 1",
intval($itm[0]['id'])
);
if($item_id)
$card_title = $item_id[0]['v'];
}
else {
notice( t('Item not found') . EOL);
return;
}
$owner = $itm[0]['uid'];
$uid = local_channel();
$observer = \App::get_observer();
$channel = channelx_by_n($owner);
if(! $channel) {
notice( t('Channel not found.') . EOL);
return;
}
$ob_hash = (($observer) ? $observer['xchan_hash'] : '');
if(! perm_is_allowed($owner,$ob_hash,'write_pages')) {
notice( t('Permission denied.') . EOL);
return;
}
$is_owner = (($uid && $uid == $owner) ? true : false);
$o = '';
$category = '';
$catsenabled = ((feature_enabled($owner,'categories')) ? 'categories' : '');
if ($catsenabled){
$itm = fetch_post_tags($itm);
$cats = get_terms_oftype($itm[0]['term'], TERM_CATEGORY);
foreach ($cats as $cat) {
if (strlen($category))
$category .= ', ';
$category .= $cat['term'];
}
}
if($itm[0]['attach']) {
$j = json_decode($itm[0]['attach'],true);
if($j) {
foreach($j as $jj) {
$itm[0]['body'] .= "\n" . '[attachment]' . basename($jj['href']) . ',' . $jj['revision'] . '[/attachment]' . "\n";
}
}
}
$mimetype = $itm[0]['mimetype'];
$content = $itm[0]['body'];
$rp = 'cards/' . $channel['channel_address'];
$x = array(
'nickname' => $channel['channel_address'],
'bbco_autocomplete'=> 'bbcode',
'return_path' => $rp,
'webpage' => ITEM_TYPE_CARD,
'button' => t('Edit'),
'writefiles' => perm_is_allowed($owner, get_observer_hash(), 'write_pages'),
'weblink' => t('Insert web link'),
'hide_voting' => false,
'hide_future' => false,
'hide_location' => false,
'hide_expire' => false,
'showacl' => true,
'acl' => populate_acl($itm[0],false,\Zotlabs\Lib\PermissionDescription::fromGlobalPermission('view_pages')),
'permissions' => $itm[0],
'lockstate' => (($itm[0]['allow_cid'] || $itm[0]['allow_gid'] || $itm[0]['deny_cid'] || $itm[0]['deny_gid']) ? 'lock' : 'unlock'),
'ptyp' => $itm[0]['type'],
'mimeselect' => false,
'mimetype' => $itm[0]['mimetype'],
'body' => undo_post_tagging($content),
'post_id' => $post_id,
'visitor' => true,
'title' => htmlspecialchars($itm[0]['title'],ENT_COMPAT,'UTF-8'),
'placeholdertitle' => t('Title (optional)'),
'pagetitle' => $card_title,
'profile_uid' => (intval($channel['channel_id'])),
'catsenabled' => $catsenabled,
'category' => $category,
'bbcode' => (($mimetype == 'text/bbcode') ? true : false)
);
$editor = status_editor($a, $x);
$o .= replace_macros(get_markup_template('edpost_head.tpl'), array(
'$title' => t('Edit Card'),
'$delete' => ((($itm[0]['author_xchan'] === $ob_hash) || ($itm[0]['owner_xchan'] === $ob_hash)) ? t('Delete') : false),
'$id' => $itm[0]['id'],
'$cancel' => t('Cancel'),
'$editor' => $editor
));
return $o;
}
}

View File

@@ -1,203 +0,0 @@
<?php
namespace Zotlabs\Module;
require_once('include/channel.php');
require_once('include/conversation.php');
require_once('include/acl_selectors.php');
class Cards extends \Zotlabs\Web\Controller {
function init() {
if(argc() > 1)
$which = argv(1);
else
return;
profile_load($which);
}
/**
* {@inheritDoc}
* @see \Zotlabs\Web\Controller::get()
*/
function get($update = 0, $load = false) {
if(observer_prohibited(true)) {
return login();
}
if(! \App::$profile) {
notice( t('Requested profile is not available.') . EOL );
\App::$error = 404;
return;
}
if(! feature_enabled(\App::$profile_uid, 'cards')) {
return;
}
nav_set_selected(t('Cards'));
head_add_link([
'rel' => 'alternate',
'type' => 'application/json+oembed',
'href' => z_root() . '/oep?f=&url=' . urlencode(z_root() . '/' . \App::$query_string),
'title' => 'oembed'
]);
$category = (($_REQUEST['cat']) ? escape_tags(trim($_REQUEST['cat'])) : '');
if($category) {
$sql_extra2 .= protect_sprintf(term_item_parent_query(\App::$profile['profile_uid'], 'item', $category, TERM_CATEGORY));
}
$which = argv(1);
$selected_card = ((argc() > 2) ? argv(2) : '');
$_SESSION['return_url'] = \App::$query_string;
$uid = local_channel();
$owner = \App::$profile_uid;
$observer = \App::get_observer();
$ob_hash = (($observer) ? $observer['xchan_hash'] : '');
if(! perm_is_allowed($owner, $ob_hash, 'view_pages')) {
notice( t('Permission denied.') . EOL);
return;
}
$is_owner = ($uid && $uid == $owner);
$channel = channelx_by_n($owner);
if($channel) {
$channel_acl = [
'allow_cid' => $channel['channel_allow_cid'],
'allow_gid' => $channel['channel_allow_gid'],
'deny_cid' => $channel['channel_deny_cid'],
'deny_gid' => $channel['channel_deny_gid']
];
}
else {
$channel_acl = [ 'allow_cid' => '', 'allow_gid' => '', 'deny_cid' => '', 'deny_gid' => '' ];
}
if(perm_is_allowed($owner, $ob_hash, 'write_pages')) {
$x = [
'webpage' => ITEM_TYPE_CARD,
'is_owner' => true,
'content_label' => t('Add Card'),
'button' => t('Create'),
'nickname' => $channel['channel_address'],
'lockstate' => (($channel['channel_allow_cid'] || $channel['channel_allow_gid']
|| $channel['channel_deny_cid'] || $channel['channel_deny_gid']) ? 'lock' : 'unlock'),
'acl' => (($is_owner) ? populate_acl($channel_acl, false,
\Zotlabs\Lib\PermissionDescription::fromGlobalPermission('view_pages')) : ''),
'permissions' => $channel_acl,
'showacl' => (($is_owner) ? true : false),
'visitor' => true,
'hide_location' => false,
'hide_voting' => false,
'profile_uid' => intval($owner),
'mimetype' => 'text/bbcode',
'mimeselect' => false,
'layoutselect' => false,
'expanded' => false,
'novoting' => false,
'catsenabled' => feature_enabled($owner, 'categories'),
'bbco_autocomplete' => 'bbcode',
'bbcode' => true
];
if($_REQUEST['title'])
$x['title'] = $_REQUEST['title'];
if($_REQUEST['body'])
$x['body'] = $_REQUEST['body'];
$editor = status_editor($a, $x);
}
else {
$editor = '';
}
$itemspage = get_pconfig(local_channel(),'system','itemspage');
\App::set_pager_itemspage(((intval($itemspage)) ? $itemspage : 20));
$pager_sql = sprintf(" LIMIT %d OFFSET %d ", intval(\App::$pager['itemspage']), intval(\App::$pager['start']));
$sql_extra = item_permissions_sql($owner);
$sql_item = '';
if($selected_card) {
$r = q("select * from iconfig where iconfig.cat = 'system' and iconfig.k = 'CARD' and iconfig.v = '%s' limit 1",
dbesc($selected_card)
);
if($r) {
$sql_item = "and item.id = " . intval($r[0]['iid']) . " ";
}
}
$r = q("select * from item
where uid = %d and item_type = %d
$sql_extra $sql_item order by item.created desc $pager_sql",
intval($owner),
intval(ITEM_TYPE_CARD)
);
$item_normal = " and item.item_hidden = 0 and item.item_type in (0,6) and item.item_deleted = 0
and item.item_unpublished = 0 and item.item_delayed = 0 and item.item_pending_remove = 0
and item.item_blocked = 0 ";
$items_result = [];
if($r) {
$pager_total = count($r);
$parents_str = ids_to_querystr($r, 'id');
$items = q("SELECT item.*, item.id AS item_id
FROM item
WHERE item.uid = %d $item_normal
AND item.parent IN ( %s )
$sql_extra $sql_extra2 ",
intval(\App::$profile['profile_uid']),
dbesc($parents_str)
);
if($items) {
xchan_query($items);
$items = fetch_post_tags($items, true);
$items_result = conv_sort($items, 'updated');
}
}
$mode = 'cards';
if(get_pconfig(local_channel(),'system','articles_list_mode') && (! $selected_card))
$page_mode = 'pager_list';
else
$page_mode = 'traditional';
$content = conversation($items_result, $mode, false, $page_mode);
$o = replace_macros(get_markup_template('cards.tpl'), [
'$title' => t('Cards'),
'$editor' => $editor,
'$content' => $content,
'$pager' => alt_pager($pager_total)
]);
return $o;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,88 +0,0 @@
<?php
namespace Zotlabs\Module;
class Changeaddr extends \Zotlabs\Web\Controller {
function post() {
if(! local_channel())
return;
if($_SESSION['delegate'])
return;
if((! x($_POST,'qxz_password')) || (! strlen(trim($_POST['qxz_password']))))
return;
if((! x($_POST,'verify')) || (! strlen(trim($_POST['verify']))))
return;
if($_POST['verify'] !== $_SESSION['remove_account_verify'])
return;
$account = \App::get_account();
$channel = \App::get_channel();
$x = account_verify_password($account['account_email'],$_POST['qxz_password']);
if(! ($x && $x['account']))
return;
if($account['account_password_changed'] > NULL_DATE) {
$d1 = datetime_convert('UTC','UTC','now - 48 hours');
if($account['account_password_changed'] > d1) {
notice( t('Channel name changes are not allowed within 48 hours of changing the account password.') . EOL);
return;
}
}
$new_address = trim($_POST['newname']);
if($new_address === $channel['channel_address'])
return;
if($new_address === 'sys') {
notice( t('Reserved nickname. Please choose another.') . EOL);
return;
}
if(check_webbie(array($new_address)) !== $new_address) {
notice( t('Nickname has unsupported characters or is already being used on this site.') . EOL);
return $ret;
}
channel_change_address($channel,$new_address);
goaway(z_root() . '/changeaddr');
}
function get() {
if(! local_channel())
goaway(z_root());
$channel = \App::get_channel();
$hash = random_string();
$_SESSION['remove_account_verify'] = $hash;
$tpl = get_markup_template('channel_rename.tpl');
$o .= replace_macros($tpl, array(
'$basedir' => z_root(),
'$hash' => $hash,
'$title' => t('Change channel nickname/address'),
'$desc' => array(t('WARNING: '), t('Any/all connections on other networks will be lost!')),
'$passwd' => t('Please enter your password for verification:'),
'$newname' => array('newname', t('New channel address'),$channel['channel_address'], ''),
'$submit' => t('Rename Channel')
));
return $o;
}
}

View File

@@ -1,7 +1,7 @@
<?php
namespace Zotlabs\Module;
require_once('include/contact_widgets.php');
require_once('include/items.php');
require_once("include/bbcode.php");
@@ -10,17 +10,10 @@ require_once('include/conversation.php');
require_once('include/acl_selectors.php');
require_once('include/permissions.php');
/**
* @brief Channel Controller
*
*/
class Channel extends \Zotlabs\Web\Controller {
function init() {
if(in_array(substr($_GET['search'],0,1),[ '@', '!', '?']))
goaway('search' . '?f=&search=' . $_GET['search']);
$which = null;
if(argc() > 1)
$which = argv(1);
@@ -41,32 +34,26 @@ class Channel extends \Zotlabs\Web\Controller {
if((local_channel()) && (argc() > 2) && (argv(2) === 'view')) {
$which = $channel['channel_address'];
$profile = argv(1);
$profile = argv(1);
}
head_add_link( [
'rel' => 'alternate',
'type' => 'application/atom+xml',
'title' => t('Posts and comments'),
'href' => z_root() . '/feed/' . $which
]);
head_add_link( [
'rel' => 'alternate',
'type' => 'application/atom+xml',
'title' => t('Only posts'),
'href' => z_root() . '/feed/' . $which . '?f=&top=1'
]);
\App::$page['htmlhead'] .= '<link rel="alternate" type="application/atom+xml" title="' . t('Posts and comments') . '" href="' . z_root() . '/feed/' . $which . '" />' . "\r\n" ;
\App::$page['htmlhead'] .= '<link rel="alternate" type="application/atom+xml" title="' . t('Only posts') . '" href="' . z_root() . '/feed/' . $which . '?top=1" />' . "\r\n" ;
// Not yet ready for prime time
// \App::$page['htmlhead'] .= '<link rel="openid.server" href="' . z_root() . '/id/' . $which .'?f=" />' . "\r\n" ;
// \App::$page['htmlhead'] .= '<link rel="openid.delegate" href="' . z_root() . '/channel/' . $which .'" />' . "\r\n" ;
// Run profile_load() here to make sure the theme is set before
// we start loading content
profile_load($which,$profile);
}
function get($update = 0, $load = false) {
if($load)
$_SESSION['loadtime'] = datetime_convert();
@@ -79,15 +66,12 @@ class Channel extends \Zotlabs\Web\Controller {
$datequery = ((x($_GET,'dend') && is_a_date_arg($_GET['dend'])) ? notags($_GET['dend']) : '');
$datequery2 = ((x($_GET,'dbegin') && is_a_date_arg($_GET['dbegin'])) ? notags($_GET['dbegin']) : '');
if(observer_prohibited(true)) {
if(observer_prohibited(true)) {
return login();
}
$category = ((x($_REQUEST,'cat')) ? $_REQUEST['cat'] : '');
$hashtags = ((x($_REQUEST,'tag')) ? $_REQUEST['tag'] : '');
$order = ((x($_GET,'order')) ? notags($_GET['order']) : 'post');
$static = ((array_key_exists('static',$_REQUEST)) ? intval($_REQUEST['static']) : 0);
$search = ((x($_GET,'search')) ? $_GET['search'] : EMPTY_STR);
$groups = array();
@@ -97,6 +81,11 @@ class Channel extends \Zotlabs\Web\Controller {
// Ensure we've got a profile owner if updating.
\App::$profile['profile_uid'] = \App::$profile_uid = $update;
}
else {
if(\App::$profile['profile_uid'] == local_channel()) {
nav_set_selected('home');
}
}
$is_owner = (((local_channel()) && (\App::$profile['profile_uid'] == local_channel())) ? true : false);
@@ -119,22 +108,15 @@ class Channel extends \Zotlabs\Web\Controller {
if(! $update) {
nav_set_selected('Channel Home');
$o .= profile_tabs($a, $is_owner, \App::$profile['channel_address']);
$static = channel_manual_conv_update(\App::$profile['profile_uid']);
// search terms header
if($search) {
$o .= replace_macros(get_markup_template("section_title.tpl"),array(
'$title' => t('Search Results For:') . ' ' . htmlspecialchars($search, ENT_COMPAT,'UTF-8')
));
}
$o .= common_friends_visitor_widget(\App::$profile['profile_uid']);
if($channel && $is_owner) {
$channel_acl = array(
'allow_cid' => $channel['channel_allow_cid'],
'allow_gid' => $channel['channel_allow_gid'],
'deny_cid' => $channel['channel_deny_cid'],
'allow_cid' => $channel['channel_allow_cid'],
'allow_gid' => $channel['channel_allow_gid'],
'deny_cid' => $channel['channel_deny_cid'],
'deny_gid' => $channel['channel_deny_gid']
);
}
@@ -160,11 +142,10 @@ class Channel extends \Zotlabs\Web\Controller {
'editor_autocomplete' => true,
'bbco_autocomplete' => 'bbcode',
'bbcode' => true,
'jotnets' => true,
'reset' => t('Reset form')
);
'jotnets' => true
);
$o .= status_editor($a,$x);
$o .= status_editor($a,$x);
}
}
@@ -175,7 +156,6 @@ class Channel extends \Zotlabs\Web\Controller {
*/
$item_normal = item_normal();
$item_normal_update = item_normal_update();
$sql_extra = item_permissions_sql(\App::$profile['profile_uid']);
if(get_pconfig(\App::$profile['profile_uid'],'system','channel_list_mode') && (! $mid))
@@ -187,48 +167,27 @@ class Channel extends \Zotlabs\Web\Controller {
$simple_update = (($update) ? " AND item_unseen = 1 " : '');
if($search) {
$search = escape_tags($search);
if(strpos($search,'#') === 0) {
$sql_extra .= term_query('item',substr($search,1),TERM_HASHTAG,TERM_COMMUNITYTAG);
}
else {
$sql_extra .= sprintf(" AND item.body like '%s' ",
dbesc(protect_sprintf('%' . $search . '%'))
);
}
}
head_add_link([
'rel' => 'alternate',
'type' => 'application/json+oembed',
'href' => z_root() . '/oep?f=&url=' . urlencode(z_root() . '/' . \App::$query_string),
'title' => 'oembed'
]);
\App::$page['htmlhead'] .= "\r\n" . '<link rel="alternate" type="application/json+oembed" href="' . z_root() . '/oep?f=&url=' . urlencode(z_root() . '/' . \App::$query_string) . '" title="oembed" />' . "\r\n";
if($update && $_SESSION['loadtime'])
$simple_update = " AND (( item_unseen = 1 AND item.changed > '" . datetime_convert('UTC','UTC',$_SESSION['loadtime']) . "' ) OR item.changed > '" . datetime_convert('UTC','UTC',$_SESSION['loadtime']) . "' ) ";
if($load)
$simple_update = '';
if($static && $simple_update)
$simple_update .= " and author_xchan = '" . protect_sprintf(get_observer_hash()) . "' ";
if(($update) && (! $load)) {
if($mid) {
$r = q("SELECT parent AS item_id from item where mid like '%s' and uid = %d $item_normal_update
$r = q("SELECT parent AS item_id from item where mid like '%s' and uid = %d $item_normal
AND item_wall = 1 $simple_update $sql_extra limit 1",
dbesc($mid . '%'),
intval(\App::$profile['profile_uid'])
);
$_SESSION['loadtime'] = datetime_convert();
}
}
else {
$r = q("SELECT parent AS item_id from item
$r = q("SELECT distinct parent AS `item_id`, created from item
left join abook on ( item.owner_xchan = abook.abook_xchan $abook_uids )
WHERE uid = %d $item_normal_update
WHERE uid = %d $item_normal
AND item_wall = 1 $simple_update
AND (abook.abook_blocked = 0 or abook.abook_flags is null)
$sql_extra
@@ -242,53 +201,43 @@ class Channel extends \Zotlabs\Web\Controller {
else {
if(x($category)) {
$sql_extra2 .= protect_sprintf(term_item_parent_query(\App::$profile['profile_uid'],'item', $category, TERM_CATEGORY));
$sql_extra .= protect_sprintf(term_query('item', $category, TERM_CATEGORY));
}
if(x($hashtags)) {
$sql_extra2 .= protect_sprintf(term_item_parent_query(\App::$profile['profile_uid'],'item', $hashtags, TERM_HASHTAG, TERM_COMMUNITYTAG));
$sql_extra .= protect_sprintf(term_query('item', $hashtags, TERM_HASHTAG, TERM_COMMUNITYTAG));
}
if($datequery) {
$sql_extra2 .= protect_sprintf(sprintf(" AND item.created <= '%s' ", dbesc(datetime_convert(date_default_timezone_get(),'',$datequery))));
$order = 'post';
}
if($datequery2) {
$sql_extra2 .= protect_sprintf(sprintf(" AND item.created >= '%s' ", dbesc(datetime_convert(date_default_timezone_get(),'',$datequery2))));
}
if($datequery || $datequery2) {
$sql_extra2 .= " and item.item_thread_top != 0 ";
}
if($order === 'post')
$ordering = "created";
else
$ordering = "commented";
$itemspage = get_pconfig(local_channel(),'system','itemspage');
\App::set_pager_itemspage(((intval($itemspage)) ? $itemspage : 20));
$pager_sql = sprintf(" LIMIT %d OFFSET %d ", intval(\App::$pager['itemspage']), intval(\App::$pager['start']));
if($load || ($checkjs->disabled())) {
if($mid) {
$r = q("SELECT parent AS item_id from item where mid like '%s' and uid = %d $item_normal
$r = q("SELECT parent AS item_id from item where mid = '%s' and uid = %d $item_normal
AND item_wall = 1 $sql_extra limit 1",
dbesc($mid . '%'),
dbesc($mid),
intval(\App::$profile['profile_uid'])
);
if (! $r) {
notice( t('Permission denied.') . EOL);
}
}
}
else {
$r = q("SELECT DISTINCT item.parent AS item_id, $ordering FROM item
left join abook on ( item.author_xchan = abook.abook_xchan $abook_uids )
WHERE true and item.uid = %d $item_normal
AND (abook.abook_blocked = 0 or abook.abook_flags is null)
AND item.item_wall = 1 AND item.item_thread_top = 1
$sql_extra $sql_extra2
ORDER BY $ordering DESC $pager_sql ",
$r = q("SELECT distinct id AS item_id, created FROM item
left join abook on item.author_xchan = abook.abook_xchan
WHERE uid = %d $item_normal
AND item_wall = 1 and item_thread_top = 1
AND (abook_blocked = 0 or abook.abook_flags is null)
$sql_extra $sql_extra2
ORDER BY created DESC $pager_sql ",
intval(\App::$profile['profile_uid'])
);
}
@@ -297,14 +246,15 @@ class Channel extends \Zotlabs\Web\Controller {
$r = array();
}
}
if($r) {
$parents_str = ids_to_querystr($r,'item_id');
$items = q("SELECT item.*, item.id AS item_id
FROM item
WHERE item.uid = %d $item_normal
AND item.parent IN ( %s )
$items = q("SELECT `item`.*, `item`.`id` AS `item_id`
FROM `item`
WHERE `item`.`uid` = %d $item_normal
AND `item`.`parent` IN ( %s )
$sql_extra ",
intval(\App::$profile['profile_uid']),
dbesc($parents_str)
@@ -312,7 +262,7 @@ class Channel extends \Zotlabs\Web\Controller {
xchan_query($items);
$items = fetch_post_tags($items, true);
$items = conv_sort($items,$ordering);
$items = conv_sort($items,'created');
if($load && $mid && (! count($items))) {
// This will happen if we don't have sufficient permissions
@@ -320,7 +270,8 @@ class Channel extends \Zotlabs\Web\Controller {
notice( t('Permission denied.') . EOL);
}
} else {
}
else {
$items = array();
}
@@ -334,7 +285,7 @@ class Channel extends \Zotlabs\Web\Controller {
$maxheight = 400;
$o .= '<div id="live-channel"></div>' . "\r\n";
$o .= "<script> var profile_uid = " . \App::$profile['profile_uid']
$o .= "<script> var profile_uid = " . \App::$profile['profile_uid']
. "; var netargs = '?f='; var profile_page = " . \App::$pager['page']
. "; divmore_height = " . intval($maxheight) . "; </script>\r\n";
@@ -344,8 +295,8 @@ class Channel extends \Zotlabs\Web\Controller {
'$uid' => ((\App::$profile['profile_uid']) ? \App::$profile['profile_uid'] : '0'),
'$gid' => '0',
'$cid' => '0',
'$cmin' => '(-1)',
'$cmax' => '(-1)',
'$cmin' => '0',
'$cmax' => '0',
'$star' => '0',
'$liked' => '0',
'$conv' => '0',
@@ -353,22 +304,20 @@ class Channel extends \Zotlabs\Web\Controller {
'$nouveau' => '0',
'$wall' => '1',
'$fh' => '0',
'$static' => $static,
'$page' => ((\App::$pager['page'] != 1) ? \App::$pager['page'] : 1),
'$search' => $search,
'$xchan' => '',
'$order' => $order,
'$search' => '',
'$order' => '',
'$list' => ((x($_REQUEST,'list')) ? intval($_REQUEST['list']) : 0),
'$file' => '',
'$cats' => (($category) ? urlencode($category) : ''),
'$tags' => (($hashtags) ? urlencode($hashtags) : ''),
'$cats' => (($category) ? $category : ''),
'$tags' => (($hashtags) ? $hashtags : ''),
'$mid' => $mid,
'$verb' => '',
'$net' => '',
'$dend' => $datequery,
'$dbegin' => $datequery2
));
}
$update_unseen = '';
@@ -376,10 +325,10 @@ class Channel extends \Zotlabs\Web\Controller {
if($page_mode === 'list') {
/**
* in "list mode", only mark the parent item and any like activities as "seen".
* in "list mode", only mark the parent item and any like activities as "seen".
* We won't distinguish between comment likes and post likes. The important thing
* is that the number of unseen comments will be accurate. The SQL to separate the
* comment likes could also get somewhat hairy.
* comment likes could also get somewhat hairy.
*/
if($parents_str) {
@@ -394,31 +343,26 @@ class Channel extends \Zotlabs\Web\Controller {
}
if($is_owner && $update_unseen) {
$x = [ 'channel_id' => local_channel(), 'update' => 'unset' ];
call_hooks('update_unseen',$x);
if($x['update'] === 'unset' || intval($x['update'])) {
$r = q("UPDATE item SET item_unseen = 0 where item_unseen = 1 and item_wall = 1 AND uid = %d $update_unseen",
intval(local_channel())
);
}
$r = q("UPDATE item SET item_unseen = 0 where item_unseen = 1 and item_wall = 1 AND uid = %d $update_unseen",
intval(local_channel())
);
}
$mode = (($search) ? 'search' : 'channel');
if($checkjs->disabled()) {
$o .= conversation($items,$mode,$update,'traditional');
}
$o .= conversation($a,$items,'channel',$update,'traditional');
}
else {
$o .= conversation($items,$mode,$update,$page_mode);
$o .= conversation($a,$items,'channel',$update,$page_mode);
}
if((! $update) || ($checkjs->disabled())) {
$o .= alt_pager(count($items));
$o .= alt_pager($a,count($items));
if ($mid && $items[0]['title'])
\App::$page['title'] = $items[0]['title'] . " - " . \App::$page['title'];
}
if($mid)
if($mid)
$o .= '<div id="content-complete"></div>';
return $o;

View File

@@ -19,7 +19,7 @@ class Chanview extends \Zotlabs\Web\Controller {
}
if($_REQUEST['address']) {
$r = q("select * from xchan where xchan_addr = '%s' limit 1",
dbesc(punify($_REQUEST['address']))
dbesc($_REQUEST['address'])
);
}
elseif(local_channel() && intval($_REQUEST['cid'])) {
@@ -58,9 +58,7 @@ class Chanview extends \Zotlabs\Web\Controller {
}
logger('mod_chanview: constructed address ' . print_r($matches,true));
}
$r = null;
if($_REQUEST['address']) {
$j = \Zotlabs\Zot\Finger::run($_REQUEST['address'],null);
if($j['success']) {
@@ -68,79 +66,40 @@ class Chanview extends \Zotlabs\Web\Controller {
$r = q("select * from xchan where xchan_addr = '%s' limit 1",
dbesc($_REQUEST['address'])
);
if($r) {
if($r)
\App::$poi = $r[0];
}
}
if(! $r) {
if(discover_by_webbie($_REQUEST['address'])) {
$r = q("select * from xchan where xchan_addr = '%s' limit 1",
dbesc($_REQUEST['address'])
);
if($r) {
\App::$poi = $r[0];
}
}
}
}
}
if(! \App::$poi) {
// We don't know who this is, and we can't figure it out from the URL
// On the plus side, there's a good chance we know somebody else at that
// hub so sending them there with a Zid will probably work anyway.
// We don't know who this is, and we can't figure it out from the URL
// On the plus side, there's a good chance we know somebody else at that
// hub so sending them there with a Zid will probably work anyway.
$url = ($_REQUEST['url']);
if(! $url) {
notice( t('Channel not found.') . EOL);
return;
}
if($observer)
$url = zid($url);
}
$is_zot = false;
$connected = false;
if (\App::$poi) {
$url = \App::$poi['xchan_url'];
if(\App::$poi['xchan_network'] === 'zot') {
$is_zot = true;
}
if(local_channel()) {
$c = q("select abook_id from abook where abook_channel = %d and abook_xchan = '%s' limit 1",
intval(local_channel()),
dbesc(\App::$poi['xchan_hash'])
);
if($c)
$connected = true;
}
$url = \App::$poi['xchan_url'];
if($observer)
$url = zid($url);
}
// let somebody over-ride the iframed viewport presentation
// or let's just declare this a failed experiment.
// if((! local_channel()) || (get_pconfig(local_channel(),'system','chanview_full')))
// We will load the chanview template if it's a foreign network,
// just so that we can provide a connect button along with a profile
// photo. Chances are we can't load the remote profile into an iframe
// because of cross-domain security headers. So provide a link to
// the remote profile.
// If we are already connected, just go to the profile.
// Zot channels will usually have a connect link.
goaway($url);
if($is_zot || $connected) {
if($is_zot && $observer) {
$url = zid($url);
}
goaway($url);
}
else {
$o = replace_macros(get_markup_template('chanview.tpl'),array(
'$url' => $url,
'$full' => t('toggle full screen mode')
));
// $o = replace_macros(get_markup_template('chanview.tpl'),array(
// '$url' => $url,
// '$full' => t('toggle full screen mode')
// ));
// return $o;
return $o;
}
}
}

View File

@@ -33,7 +33,9 @@ class Chat extends \Zotlabs\Web\Controller {
$which = $channel['channel_address'];
$profile = argv(1);
}
\App::$page['htmlhead'] .= '<link rel="alternate" type="application/atom+xml" href="' . z_root() . '/feed/' . $which .'" />' . "\r\n" ;
// Run profile_load() here to make sure the theme is set before
// we start loading content
@@ -89,11 +91,9 @@ class Chat extends \Zotlabs\Web\Controller {
function get() {
if(local_channel()) {
if(local_channel())
$channel = \App::get_channel();
nav_set_selected('My Chatrooms');
}
$ob = \App::get_observer();
$observer = get_observer_hash();
if(! $observer) {
@@ -212,8 +212,7 @@ class Chat extends \Zotlabs\Web\Controller {
require_once('include/conversation.php');
//$o = profile_tabs($a,((local_channel() && local_channel() == \App::$profile['profile_uid']) ? true : false),\App::$profile['channel_address']);
$o = '';
$o = profile_tabs($a,((local_channel() && local_channel() == \App::$profile['profile_uid']) ? true : false),\App::$profile['channel_address']);
if(! feature_enabled(\App::$profile['profile_uid'],'ajaxchat')) {
notice( t('Feature disabled.') . EOL);

View File

@@ -60,7 +60,7 @@ class Chatsvc extends \Zotlabs\Web\Controller {
intval(\App::$data['chat']['room_id']),
dbesc(get_observer_hash()),
dbesc(datetime_convert()),
dbesc(str_rot47(base64url_encode($arr['chat_text'])))
dbesc($arr['chat_text'])
);
$ret['success'] = true;
@@ -111,22 +111,8 @@ class Chatsvc extends \Zotlabs\Web\Controller {
intval(\App::$data['chat']['room_id'])
);
if($r) {
foreach($r as $rv) {
if(! $rv['xchan_name']) {
$rv['xchan_hash'] = $rv['cp_xchan'];
$rv['xchan_name'] = substr($rv['cp_xchan'],strrpos($rv['cp_xchan'],'.')+1);
$rv['xchan_addr'] = '';
$rv['xchan_network'] = 'unknown';
$rv['xchan_url'] = z_root();
$rv['xchan_hidden'] = 1;
$rv['xchan_photo_mimetype'] = 'image/png';
$rv['xchan_photo_l'] = z_root() . '/' . get_default_profile_photo(300);
$rv['xchan_photo_m'] = z_root() . '/' . get_default_profile_photo(80);
$rv['xchan_photo_s'] = z_root() . '/' . get_default_profile_photo(48);
}
switch($rv['cp_status']) {
foreach($r as $rr) {
switch($rr['cp_status']) {
case 'away':
$status = t('Away');
$status_class = 'away';
@@ -138,7 +124,7 @@ class Chatsvc extends \Zotlabs\Web\Controller {
break;
}
$inroom[] = array('img' => zid($rv['xchan_photo_m']), 'img_type' => $rv['xchan_photo_mimetype'],'name' => $rv['xchan_name'], 'status' => $status, 'status_class' => $status_class);
$inroom[] = array('img' => zid($rr['xchan_photo_m']), 'img_type' => $rr['xchan_photo_mimetype'],'name' => $rr['xchan_name'], 'status' => $status, 'status_class' => $status_class);
}
}
@@ -157,7 +143,7 @@ class Chatsvc extends \Zotlabs\Web\Controller {
'name' => $rr['xchan_name'],
'isotime' => datetime_convert('UTC', date_default_timezone_get(), $rr['created'], 'c'),
'localtime' => datetime_convert('UTC', date_default_timezone_get(), $rr['created'], 'r'),
'text' => zidify_links(smilies(bbcode(base64url_decode(str_rot47($rr['chat_text']))))),
'text' => smilies(bbcode($rr['chat_text'])),
'self' => ((get_observer_hash() == $rr['chat_xchan']) ? 'self' : '')
);
}

View File

@@ -1,7 +1,7 @@
<?php
namespace Zotlabs\Module;
/**
* @file Zotlabs/Module/Cloud.php
* @file mod/cloud.php
* @brief Initialize Hubzilla's cloud (SabreDAV).
*
* Module for accessing the DAV storage area.
@@ -17,42 +17,34 @@ require_once('include/attach.php');
/**
* @brief Cloud Module.
* @brief Fires up the SabreDAV server.
*
* @param App &$a
*/
class Cloud extends \Zotlabs\Web\Controller {
/**
* @brief Fires up the SabreDAV server.
*
*/
function init() {
if (! is_dir('store'))
os_mkdir('store', STORAGE_DEFAULT_PERMISSIONS, false);
$which = null;
if (argc() > 1)
$which = argv(1);
if (argc() < 2 && intval(get_config('system','cloud_disable_siteroot'))) {
notice( t('Permission denied.') . EOL);
construct_page();
killme();
}
$profile = 0;
\App::$page['htmlhead'] .= '<link rel="alternate" type="application/atom+xml" href="' . z_root() . '/feed/' . $which . '" />' . "\r\n";
if ($which)
profile_load( $which, $profile);
$auth = new \Zotlabs\Storage\BasicAuth();
$ob_hash = get_observer_hash();
if ($ob_hash) {
if (local_channel()) {
$channel = \App::get_channel();
@@ -65,78 +57,47 @@ class Cloud extends \Zotlabs\Web\Controller {
}
$auth->observer = $ob_hash;
}
// if we arrived at this path with any query parameters in the url, build a clean url without
// them and redirect.
if(! array_key_exists('cloud_sort',$_SESSION)) {
$_SESSION['cloud_sort'] = 'name';
}
$_SESSION['cloud_sort'] = (($_REQUEST['sort']) ? trim(notags($_REQUEST['sort'])) : $_SESSION['cloud_sort']);
$x = clean_query_string();
if($x !== \App::$query_string)
goaway(z_root() . '/' . $x);
if ($_GET['davguest'])
$_SESSION['davguest'] = true;
$_SERVER['QUERY_STRING'] = str_replace(array('?f=', '&f='), array('', ''), $_SERVER['QUERY_STRING']);
$_SERVER['QUERY_STRING'] = strip_zids($_SERVER['QUERY_STRING']);
$_SERVER['QUERY_STRING'] = preg_replace('/[\?&]davguest=(.*?)([\?&]|$)/ism', '', $_SERVER['QUERY_STRING']);
$_SERVER['REQUEST_URI'] = str_replace(array('?f=', '&f='), array('', ''), $_SERVER['REQUEST_URI']);
$_SERVER['REQUEST_URI'] = strip_zids($_SERVER['REQUEST_URI']);
$_SERVER['REQUEST_URI'] = preg_replace('/[\?&]davguest=(.*?)([\?&]|$)/ism', '', $_SERVER['REQUEST_URI']);
$rootDirectory = new \Zotlabs\Storage\Directory('/', $auth);
// A SabreDAV server-object
$server = new SDAV\Server($rootDirectory);
// prevent overwriting changes each other with a lock backend
$lockBackend = new SDAV\Locks\Backend\File('store/[data]/locks');
$lockPlugin = new SDAV\Locks\Plugin($lockBackend);
$server->addPlugin($lockPlugin);
$is_readable = false;
// provide a directory view for the cloud in Hubzilla
$browser = new \Zotlabs\Storage\Browser($auth);
$auth->setBrowserPlugin($browser);
$server->addPlugin($browser);
// Experimental QuotaPlugin
// require_once('\Zotlabs\Storage/QuotaPlugin.php');
// $server->addPlugin(new \Zotlabs\Storage\\QuotaPlugin($auth));
// over-ride the default XML output on thrown exceptions
$server->on('exception', [ $this, 'DAVException' ]);
// require_once('\Zotlabs\Storage/QuotaPlugin.php');
// $server->addPlugin(new \Zotlabs\Storage\\QuotaPlugin($auth));
ob_start();
// All we need to do now, is to fire up the server
$server->exec();
if($browser->build_page)
construct_page();
ob_end_flush();
killme();
}
function DAVException($err) {
if($err instanceof \Sabre\DAV\Exception\NotFound) {
notice( t('Not found') . EOL);
}
elseif($err instanceof \Sabre\DAV\Exception\Forbidden) {
notice( t('Permission denied') . EOL);
}
elseif($err instanceof \Sabre\DAV\Exception\NotImplemented) {
notice( t('Please refresh page') . EOL);
}
else {
notice( t('Unknown error') . EOL);
}
construct_page();
killme();
}
}

View File

@@ -1,21 +0,0 @@
<?php
namespace Zotlabs\Module;
class Cloud_tiles extends \Zotlabs\Web\Controller {
function init() {
if(intval($_SESSION['cloud_tiles']))
$_SESSION['cloud_tiles'] = 0;
else
$_SESSION['cloud_tiles'] = 1;
if(local_channel()) {
set_pconfig(local_channel(),'system','cloud_tiles',$_SESSION['cloud_tiles']);
}
goaway(z_root() . '/' . hex2bin(argv(1)));
}
}

View File

@@ -25,7 +25,7 @@ class Common extends \Zotlabs\Web\Controller {
}
function get() {
function get() {
$o = '';
@@ -34,37 +34,38 @@ class Common extends \Zotlabs\Web\Controller {
$observer_hash = get_observer_hash();
if(! perm_is_allowed(\App::$profile['profile_uid'],$observer_hash,'view_contacts')) {
notice( t('Permission denied.') . EOL);
return;
}
$o .= '<h2>' . t('Common connections') . '</h2>';
$t = count_common_friends(\App::$profile['profile_uid'],$observer_hash);
if(! $t) {
notice( t('No connections in common.') . EOL);
return;
return $o;
}
$r = common_friends(\App::$profile['profile_uid'],$observer_hash);
if($r) {
$tpl = get_markup_template('common_friends.tpl');
foreach($r as $rr) {
$items[] = [
'url' => $rr['xchan_url'],
'name' => $rr['xchan_name'],
'photo' => $rr['xchan_photo_m'],
'tags' => ''
];
$o .= replace_macros($tpl,array(
'$url' => $rr['xchan_url'],
'$name' => $rr['xchan_name'],
'$photo' => $rr['xchan_photo_m'],
'$tags' => ''
));
}
$o .= cleardiv();
}
$tpl = get_markup_template('common_friends.tpl');
$o = replace_macros($tpl, [
'$title' => t('View Common Connections'),
'$items' => $items
]);
return $o;
}

View File

@@ -29,7 +29,7 @@ class Connect extends \Zotlabs\Web\Controller {
profile_load($which,'');
}
function post() {
function post() {
if(! array_key_exists('channel', \App::$data))
return;
@@ -78,7 +78,7 @@ class Connect extends \Zotlabs\Web\Controller {
function get() {
function get() {
$edit = ((local_channel() && (local_channel() == \App::$data['channel']['channel_id'])) ? true : false);

View File

@@ -5,6 +5,10 @@ namespace Zotlabs\Module;
require_once('include/socgraph.php');
require_once('include/selectors.php');
require_once('include/group.php');
require_once('include/contact_widgets.php');
require_once('include/zot.php');
require_once('include/widgets.php');
class Connections extends \Zotlabs\Web\Controller {
@@ -19,7 +23,7 @@ class Connections extends \Zotlabs\Web\Controller {
}
function get() {
function get() {
$sort_type = 0;
$o = '';
@@ -29,10 +33,7 @@ class Connections extends \Zotlabs\Web\Controller {
notice( t('Permission denied.') . EOL);
return login();
}
nav_set_selected('Connections');
$active = false;
$blocked = false;
$hidden = false;
$ignored = false;
@@ -45,16 +46,11 @@ class Connections extends \Zotlabs\Web\Controller {
if(! $_REQUEST['aj'])
$_SESSION['return_url'] = \App::$query_string;
$search_flags = "";
$search_flags = '';
$head = '';
if(argc() == 2) {
switch(argv(1)) {
case 'active':
$search_flags = " and abook_blocked = 0 and abook_ignored = 0 and abook_hidden = 0 and abook_archived = 0 AND abook_not_here = 0 ";
$head = t('Active');
$active = true;
break;
case 'blocked':
$search_flags = " and abook_blocked = 1 ";
$head = t('Blocked');
@@ -71,14 +67,15 @@ class Connections extends \Zotlabs\Web\Controller {
$hidden = true;
break;
case 'archived':
$search_flags = " and ( abook_archived = 1 OR abook_not_here = 1) ";
$head = t('Archived/Unreachable');
$search_flags = " and abook_archived = 1 ";
$head = t('Archived');
$archived = true;
break;
case 'pending':
$search_flags = " and abook_pending = 1 ";
$head = t('New');
$pending = true;
nav_set_selected('intros');
break;
case 'ifpending':
$r = q("SELECT COUNT(abook.abook_id) AS total FROM abook left join xchan on abook.abook_xchan = xchan.xchan_hash where abook_channel = %d and abook_pending = 1 and abook_self = 0 and abook_ignored = 0 and xchan_deleted = 0 and xchan_orphan = 0 ",
@@ -88,6 +85,7 @@ class Connections extends \Zotlabs\Web\Controller {
$search_flags = " and abook_pending = 1 ";
$head = t('New');
$pending = true;
nav_set_selected('intros');
\App::$argv[1] = 'pending';
}
else {
@@ -97,6 +95,7 @@ class Connections extends \Zotlabs\Web\Controller {
\App::$argc = 1;
unset(\App::$argv[1]);
}
nav_set_selected('intros');
break;
// case 'unconnected':
// $search_flags = " and abook_unconnected = 1 ";
@@ -107,9 +106,8 @@ class Connections extends \Zotlabs\Web\Controller {
case 'all':
$head = t('All');
default:
$search_flags = " and abook_blocked = 0 and abook_ignored = 0 and abook_hidden = 0 and abook_archived = 0 and abook_not_here = 0 ";
$active = true;
$head = t('Active');
$search_flags = '';
$all = true;
break;
}
@@ -136,13 +134,6 @@ class Connections extends \Zotlabs\Web\Controller {
),
*/
'active' => array(
'label' => t('Active Connections'),
'url' => z_root() . '/connections/active',
'sel' => ($active) ? 'active' : '',
'title' => t('Show active connections'),
),
'pending' => array(
'label' => t('New Connections'),
'url' => z_root() . '/connections/pending',
@@ -150,6 +141,12 @@ class Connections extends \Zotlabs\Web\Controller {
'title' => t('Show pending (new) connections'),
),
'all' => array(
'label' => t('All Connections'),
'url' => z_root() . '/connections/all',
'sel' => ($all) ? 'active' : '',
'title' => t('Show all connections'),
),
/*
array(
@@ -175,10 +172,10 @@ class Connections extends \Zotlabs\Web\Controller {
),
'archived' => array(
'label' => t('Archived/Unreachable'),
'label' => t('Archived'),
'url' => z_root() . '/connections/archived',
'sel' => ($archived) ? 'active' : '',
'title' => t('Only show archived/unreachable connections'),
'title' => t('Only show archived connections'),
),
'hidden' => array(
@@ -195,13 +192,6 @@ class Connections extends \Zotlabs\Web\Controller {
// 'title' => t('Only show one-way connections'),
// ),
'all' => array(
'label' => t('All Connections'),
'url' => z_root() . '/connections',
'sel' => ($all) ? 'active' : '',
'title' => t('Show all connections'),
),
);
@@ -238,34 +228,19 @@ class Connections extends \Zotlabs\Web\Controller {
$contacts = array();
if($r) {
vcard_query($r);
if(count($r)) {
foreach($r as $rr) {
if($rr['xchan_url']) {
if(($rr['vcard']) && is_array($rr['vcard']['tels']) && $rr['vcard']['tels'][0]['nr'])
$phone = $rr['vcard']['tels'][0]['nr'];
else
$phone = '';
$status_str = '';
$status = array(
((intval($rr['abook_active'])) ? t('Active') : ''),
((intval($rr['abook_pending'])) ? t('Pending approval') : ''),
((intval($rr['abook_archived'])) ? t('Archived') : ''),
((intval($rr['abook_hidden'])) ? t('Hidden') : ''),
((intval($rr['abook_ignored'])) ? t('Ignored') : ''),
((intval($rr['abook_blocked'])) ? t('Blocked') : ''),
((intval($rr['abook_not_here'])) ? t('Not connected at this location') : '')
((intval($rr['abook_blocked'])) ? t('Blocked') : '')
);
$oneway = false;
if(! intval(get_abconfig(local_channel(),$rr['xchan_hash'],'their_perms','post_comments'))) {
$oneway = true;
}
foreach($status as $str) {
if(!$str)
@@ -278,23 +253,20 @@ class Connections extends \Zotlabs\Web\Controller {
$contacts[] = array(
'img_hover' => sprintf( t('%1$s [%2$s]'),$rr['xchan_name'],$rr['xchan_url']),
'edit_hover' => t('Edit connection'),
'edit' => t('Edit'),
'delete_hover' => t('Delete connection'),
'id' => $rr['abook_id'],
'thumb' => $rr['xchan_photo_m'],
'name' => $rr['xchan_name'],
'classes' => ((intval($rr['abook_archived']) || intval($rr['abook_not_here'])) ? 'archived' : ''),
'classes' => (intval($rr['abook_archived']) ? 'archived' : ''),
'link' => z_root() . '/connedit/' . $rr['abook_id'],
'deletelink' => z_root() . '/connedit/' . intval($rr['abook_id']) . '/drop',
'delete' => t('Delete'),
'url' => chanlink_hash($rr['xchan_hash']),
'url' => chanlink_url($rr['xchan_url']),
'webbie_label' => t('Channel address'),
'webbie' => $rr['xchan_addr'],
'network_label' => t('Network'),
'network' => network_to_name($rr['xchan_network']),
'public_forum' => ((intval($rr['xchan_pubforum'])) ? true : false),
'call' => t('Call'),
'phone' => $phone,
'status_label' => t('Status'),
'status' => $status_str,
'connected_label' => t('Connected'),
@@ -304,8 +276,7 @@ class Connections extends \Zotlabs\Web\Controller {
'ignore_hover' => t('Ignore connection'),
'ignore' => ((! $rr['abook_ignored']) ? t('Ignore') : false),
'recent_label' => t('Recent activity'),
'recentlink' => z_root() . '/network/?f=&cid=' . intval($rr['abook_id']),
'oneway' => $oneway
'recentlink' => z_root() . '/network/?f=&cid=' . intval($rr['abook_id'])
);
}
}
@@ -326,7 +297,7 @@ class Connections extends \Zotlabs\Web\Controller {
killme();
}
else {
$o .= "<script> var page_query = '" . escape_tags($_GET['q']) . "'; var extra_args = '" . extra_query_args() . "' ; </script>";
$o .= "<script> var page_query = '" . $_GET['q'] . "'; var extra_args = '" . extra_query_args() . "' ; </script>";
$o .= replace_macros(get_markup_template('connections.tpl'),array(
'$header' => t('Connections') . (($head) ? ': ' . $head : ''),
'$tabs' => $tabs,

Some files were not shown because too many files have changed in this diff Show More