├── .buildpacks
├── .env.example
├── .gitattributes
├── .gitignore
├── Docker
├── ProxyNginx.conf
├── entrypoint.sh
└── php-fpm.conf
├── Dockerfile
├── LICENSE.txt
├── Procfile
├── README.md
├── app
├── Console
│ ├── Commands
│ │ └── .gitkeep
│ └── Kernel.php
├── Exceptions
│ └── Handler.php
├── Http
│ ├── Controllers
│ │ ├── Controller.php
│ │ ├── DashLicenseUtil.php
│ │ ├── EntriesController.php
│ │ ├── ForgotController.php
│ │ ├── TeamsController.php
│ │ └── UsersController.php
│ ├── Middleware
│ │ └── ExampleMiddleware.php
│ └── routes.php
├── Jobs
│ └── Job.php
├── Models
│ ├── Entry.php
│ ├── Identifier.php
│ ├── License.php
│ ├── Team.php
│ ├── User.php
│ └── Vote.php
├── Notifications
│ └── ResetPasswordNotification.php
└── Providers
│ └── AppServiceProvider.php
├── artisan
├── bootstrap
└── app.php
├── composer.json
├── composer.lock
├── config
├── app.php
├── auth.php
├── cache.php
├── database.php
├── mail.php
└── session.php
├── database
├── migrations
│ ├── .gitkeep
│ ├── 2015_01_18_140441_create_users_table.php
│ ├── 2015_01_18_155040_create_password_reminders_table.php
│ ├── 2015_01_22_150131_create_teams_table.php
│ └── 2015_01_26_162713_create_entries_table.php
└── seeds
│ └── DatabaseSeeder.php
├── docker-compose.yml
├── nginx.conf
├── phpunit.xml
├── public
├── .htaccess
└── index.php
├── requirements.txt
├── resources
├── lang
│ └── en
│ │ └── validation.php
└── views
│ ├── .gitkeep
│ └── emails
│ └── auth
│ └── reminder.blade.php
├── runtime.txt
├── server.php
├── storage
├── app
│ └── .gitignore
├── framework
│ ├── cache
│ │ └── .gitignore
│ ├── sessions
│ │ └── .gitignore
│ └── views
│ │ └── .gitignore
└── logs
│ └── .gitignore
└── tests
├── ExampleTest.php
└── TestCase.php
/.buildpacks:
--------------------------------------------------------------------------------
1 | https://github.com/heroku/heroku-buildpack-php
2 | https://github.com/heroku/heroku-buildpack-python
3 |
--------------------------------------------------------------------------------
/.env.example:
--------------------------------------------------------------------------------
1 | APP_KEY=SomeRandomKey!
2 |
3 | APP_ENV=production
4 | APP_DEBUG=false
5 | APP_LOCALE=en
6 | APP_FALLBACK_LOCALE=en
7 |
8 | DB_CONNECTION=mysql
9 | DB_HOST=localhost
10 | DB_DATABASE=annotations
11 | DB_USERNAME=mysql_username
12 | DB_PASSWORD=mysql_password
13 |
14 | CACHE_DRIVER=file
15 | SESSION_DRIVER=file
16 | QUEUE_DRIVER=sync
17 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | * text=auto
2 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /lumen.log
2 | /vendor
3 | .env
4 | .env.production
5 | /node_modules
6 | /bootstrap/compiled.php
7 | .env.*.php
8 | .env.php
9 | annotations.dkim.*
10 | .DS_Store
11 | Thumbs.db
12 | Annotations.paw
13 | /storage/framework/compiled.php
14 |
15 | # Claude Code files
16 | CLAUDE.md
17 | .claude/
18 |
--------------------------------------------------------------------------------
/Docker/ProxyNginx.conf:
--------------------------------------------------------------------------------
1 | upstream dash-handler {
2 | server 127.0.0.1:9002;
3 | }
4 |
5 | server {
6 | listen 80;
7 | server_name dash.example.com;
8 | root /var/www/public;
9 |
10 | # Point index to the Laravel front controller.
11 | index index.php;
12 |
13 | location / {
14 | # URLs to attempt, including pretty ones.
15 | try_files $uri $uri/ /index.php?$query_string;
16 | }
17 |
18 | location ~ \.php(?:$|/) {
19 | fastcgi_split_path_info ^(.+\.php)(/.+)$;
20 | include fastcgi_params;
21 | fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
22 | fastcgi_param PATH_INFO $fastcgi_path_info;
23 | fastcgi_pass dash-handler;
24 | }
25 |
26 | # Remove trailing slash to please routing system.
27 | if (!-d $request_filename) {
28 | rewrite ^/(.+)/$ /$1 permanent;
29 | }
30 |
31 | # We don't need .ht files with nginx.
32 | location ~ /\.ht {
33 | deny all;
34 | }
35 |
36 | # Set header expirations on per-project basis
37 | location ~* \.(?:ico|css|js|jpe?g|JPG|png|svg|woff)$ {
38 | expires 365d;
39 | }
40 |
41 | }
42 |
--------------------------------------------------------------------------------
/Docker/entrypoint.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | cat > .env <<-EOF
4 | APP_KEY=${APP_KEY:-SomeRandomKey!}
5 | APP_ENV=${APP_ENV:-local}
6 | APP_DEBUG=${APP_ENV:-true}
7 | APP_LOCALE=${APP_LOCALE:-en}
8 | APP_FALLBACK_LOCALE=${APP_FALLBACK_LOCALE:-en}
9 |
10 | DB_CONNECTION=${DB_CONNECTION:-mysql}
11 | DB_HOST=${DB_PORT_3306_TCP_ADDR:-localhost}
12 | DB_DATABASE=${DB_DATABASE:-annotations}
13 | DB_USERNAME=${DB_USER:-mysql_username}
14 | DB_PASSWORD=${DB_PASSWORD:-mysql_password}
15 |
16 | CACHE_DRIVER=${CACHE_DRIVER:-file}
17 | SESSION_DRIVER=${SESSION_DRIVER:-file}
18 | QUEUE_DRIVER=${QUEUE_DRIVER:-sync}
19 | EOF
20 | composer config -g github-oauth.github.com ${TOKEN:-}
21 | composer install
22 | php artisan cache:clear
23 | chmod -R 777 public
24 | chmod -R 777 storage
25 | php artisan migrate --force
26 | exec php-fpm
27 |
--------------------------------------------------------------------------------
/Docker/php-fpm.conf:
--------------------------------------------------------------------------------
1 | ;;;;;;;;;;;;;;;;;;;;;
2 | ; FPM Configuration ;
3 | ;;;;;;;;;;;;;;;;;;;;;
4 |
5 | ; All relative paths in this configuration file are relative to PHP's install
6 | ; prefix (/usr). This prefix can be dynamically changed by using the
7 | ; '-p' argument from the command line.
8 |
9 | ; Include one or more files. If glob(3) exists, it is used to include a bunch of
10 | ; files from a glob(3) pattern. This directive can be used everywhere in the
11 | ; file.
12 | ; Relative path can also be used. They will be prefixed by:
13 | ; - the global prefix if it's been set (-p argument)
14 | ; - /usr otherwise
15 | ;include=etc/fpm.d/*.conf
16 |
17 | ;;;;;;;;;;;;;;;;;;
18 | ; Global Options ;
19 | ;;;;;;;;;;;;;;;;;;
20 |
21 | [global]
22 | ; Pid file
23 | ; Note: the default prefix is /var
24 | ; Default Value: none
25 | ;pid = run/php-fpm.pid
26 |
27 | ; Error log file
28 | ; If it's set to "syslog", log is sent to syslogd instead of being written
29 | ; in a local file.
30 | ; Note: the default prefix is /var
31 | ; Default Value: log/php-fpm.log
32 | error_log = /proc/self/fd/2
33 |
34 | ; syslog_facility is used to specify what type of program is logging the
35 | ; message. This lets syslogd specify that messages from different facilities
36 | ; will be handled differently.
37 | ; See syslog(3) for possible values (ex daemon equiv LOG_DAEMON)
38 | ; Default Value: daemon
39 | ;syslog.facility = daemon
40 |
41 | ; syslog_ident is prepended to every message. If you have multiple FPM
42 | ; instances running on the same server, you can change the default value
43 | ; which must suit common needs.
44 | ; Default Value: php-fpm
45 | ;syslog.ident = php-fpm
46 |
47 | ; Log level
48 | ; Possible Values: alert, error, warning, notice, debug
49 | ; Default Value: notice
50 | ;log_level = notice
51 |
52 | ; If this number of child processes exit with SIGSEGV or SIGBUS within the time
53 | ; interval set by emergency_restart_interval then FPM will restart. A value
54 | ; of '0' means 'Off'.
55 | ; Default Value: 0
56 | ;emergency_restart_threshold = 0
57 |
58 | ; Interval of time used by emergency_restart_interval to determine when
59 | ; a graceful restart will be initiated. This can be useful to work around
60 | ; accidental corruptions in an accelerator's shared memory.
61 | ; Available Units: s(econds), m(inutes), h(ours), or d(ays)
62 | ; Default Unit: seconds
63 | ; Default Value: 0
64 | ;emergency_restart_interval = 0
65 |
66 | ; Time limit for child processes to wait for a reaction on signals from master.
67 | ; Available units: s(econds), m(inutes), h(ours), or d(ays)
68 | ; Default Unit: seconds
69 | ; Default Value: 0
70 | ;process_control_timeout = 0
71 |
72 | ; The maximum number of processes FPM will fork. This has been design to control
73 | ; the global number of processes when using dynamic PM within a lot of pools.
74 | ; Use it with caution.
75 | ; Note: A value of 0 indicates no limit
76 | ; Default Value: 0
77 | ; process.max = 128
78 |
79 | ; Specify the nice(2) priority to apply to the master process (only if set)
80 | ; The value can vary from -19 (highest priority) to 20 (lower priority)
81 | ; Note: - It will only work if the FPM master process is launched as root
82 | ; - The pool process will inherit the master process priority
83 | ; unless it specified otherwise
84 | ; Default Value: no set
85 | ; process.priority = -19
86 |
87 | ; Send FPM to background. Set to 'no' to keep FPM in foreground for debugging.
88 | ; Default Value: yes
89 | daemonize = no
90 |
91 | ; Set open file descriptor rlimit for the master process.
92 | ; Default Value: system defined value
93 | ;rlimit_files = 1024
94 |
95 | ; Set max core size rlimit for the master process.
96 | ; Possible Values: 'unlimited' or an integer greater or equal to 0
97 | ; Default Value: system defined value
98 | ;rlimit_core = 0
99 |
100 | ; Specify the event mechanism FPM will use. The following is available:
101 | ; - select (any POSIX os)
102 | ; - poll (any POSIX os)
103 | ; - epoll (linux >= 2.5.44)
104 | ; - kqueue (FreeBSD >= 4.1, OpenBSD >= 2.9, NetBSD >= 2.0)
105 | ; - /dev/poll (Solaris >= 7)
106 | ; - port (Solaris >= 10)
107 | ; Default Value: not set (auto detection)
108 | ;events.mechanism = epoll
109 |
110 | ; When FPM is build with systemd integration, specify the interval,
111 | ; in second, between health report notification to systemd.
112 | ; Set to 0 to disable.
113 | ; Available Units: s(econds), m(inutes), h(ours)
114 | ; Default Unit: seconds
115 | ; Default value: 10
116 | ;systemd_interval = 10
117 |
118 | ;;;;;;;;;;;;;;;;;;;;
119 | ; Pool Definitions ;
120 | ;;;;;;;;;;;;;;;;;;;;
121 |
122 | ; Multiple pools of child processes may be started with different listening
123 | ; ports and different management options. The name of the pool will be
124 | ; used in logs and stats. There is no limitation on the number of pools which
125 | ; FPM can handle. Your system will tell you anyway :)
126 |
127 | ; Start a new pool named 'www'.
128 | ; the variable $pool can we used in any directive and will be replaced by the
129 | ; pool name ('www' here)
130 | [www]
131 |
132 | ; Per pool prefix
133 | ; It only applies on the following directives:
134 | ; - 'access.log'
135 | ; - 'slowlog'
136 | ; - 'listen' (unixsocket)
137 | ; - 'chroot'
138 | ; - 'chdir'
139 | ; - 'php_values'
140 | ; - 'php_admin_values'
141 | ; When not set, the global prefix (or /usr) applies instead.
142 | ; Note: This directive can also be relative to the global prefix.
143 | ; Default Value: none
144 | ;prefix = /path/to/pools/$pool
145 |
146 | ; Unix user/group of processes
147 | ; Note: The user is mandatory. If the group is not set, the default user's group
148 | ; will be used.
149 | user = nobody
150 | group = nobody
151 |
152 | ; The address on which to accept FastCGI requests.
153 | ; Valid syntaxes are:
154 | ; 'ip.add.re.ss:port' - to listen on a TCP socket to a specific IPv4 address on
155 | ; a specific port;
156 | ; '[ip:6:addr:ess]:port' - to listen on a TCP socket to a specific IPv6 address on
157 | ; a specific port;
158 | ; 'port' - to listen on a TCP socket to all IPv4 addresses on a
159 | ; specific port;
160 | ; '[::]:port' - to listen on a TCP socket to all addresses
161 | ; (IPv6 and IPv4-mapped) on a specific port;
162 | ; '/path/to/unix/socket' - to listen on a unix socket.
163 | ; Note: This value is mandatory.
164 | listen = 0.0.0.0:9000
165 |
166 | ; Set listen(2) backlog.
167 | ; Default Value: 65535 (-1 on FreeBSD and OpenBSD)
168 | ;listen.backlog = 65535
169 |
170 | ; Set permissions for unix socket, if one is used. In Linux, read/write
171 | ; permissions must be set in order to allow connections from a web server. Many
172 | ; BSD-derived systems allow connections regardless of permissions.
173 | ; Default Values: user and group are set as the running user
174 | ; mode is set to 0660
175 | ;listen.owner = nobody
176 | ;listen.group = nobody
177 | ;listen.mode = 0660
178 | ; When POSIX Access Control Lists are supported you can set them using
179 | ; these options, value is a comma separated list of user/group names.
180 | ; When set, listen.owner and listen.group are ignored
181 | ;listen.acl_users =
182 | ;listen.acl_groups =
183 |
184 | ; List of addresses (IPv4/IPv6) of FastCGI clients which are allowed to connect.
185 | ; Equivalent to the FCGI_WEB_SERVER_ADDRS environment variable in the original
186 | ; PHP FCGI (5.2.2+). Makes sense only with a tcp listening socket. Each address
187 | ; must be separated by a comma. If this value is left blank, connections will be
188 | ; accepted from any ip address.
189 | ; Default Value: any
190 | ;;listen.allowed_clients = 127.0.0.1
191 |
192 | ; Specify the nice(2) priority to apply to the pool processes (only if set)
193 | ; The value can vary from -19 (highest priority) to 20 (lower priority)
194 | ; Note: - It will only work if the FPM master process is launched as root
195 | ; - The pool processes will inherit the master process priority
196 | ; unless it specified otherwise
197 | ; Default Value: no set
198 | ; process.priority = -19
199 |
200 | ; Choose how the process manager will control the number of child processes.
201 | ; Possible Values:
202 | ; static - a fixed number (pm.max_children) of child processes;
203 | ; dynamic - the number of child processes are set dynamically based on the
204 | ; following directives. With this process management, there will be
205 | ; always at least 1 children.
206 | ; pm.max_children - the maximum number of children that can
207 | ; be alive at the same time.
208 | ; pm.start_servers - the number of children created on startup.
209 | ; pm.min_spare_servers - the minimum number of children in 'idle'
210 | ; state (waiting to process). If the number
211 | ; of 'idle' processes is less than this
212 | ; number then some children will be created.
213 | ; pm.max_spare_servers - the maximum number of children in 'idle'
214 | ; state (waiting to process). If the number
215 | ; of 'idle' processes is greater than this
216 | ; number then some children will be killed.
217 | ; ondemand - no children are created at startup. Children will be forked when
218 | ; new requests will connect. The following parameter are used:
219 | ; pm.max_children - the maximum number of children that
220 | ; can be alive at the same time.
221 | ; pm.process_idle_timeout - The number of seconds after which
222 | ; an idle process will be killed.
223 | ; Note: This value is mandatory.
224 | pm = dynamic
225 |
226 | ; The number of child processes to be created when pm is set to 'static' and the
227 | ; maximum number of child processes when pm is set to 'dynamic' or 'ondemand'.
228 | ; This value sets the limit on the number of simultaneous requests that will be
229 | ; served. Equivalent to the ApacheMaxClients directive with mpm_prefork.
230 | ; Equivalent to the PHP_FCGI_CHILDREN environment variable in the original PHP
231 | ; CGI. The below defaults are based on a server without much resources. Don't
232 | ; forget to tweak pm.* to fit your needs.
233 | ; Note: Used when pm is set to 'static', 'dynamic' or 'ondemand'
234 | ; Note: This value is mandatory.
235 | pm.max_children = 5
236 |
237 | ; The number of child processes created on startup.
238 | ; Note: Used only when pm is set to 'dynamic'
239 | ; Default Value: min_spare_servers + (max_spare_servers - min_spare_servers) / 2
240 | pm.start_servers = 2
241 |
242 | ; The desired minimum number of idle server processes.
243 | ; Note: Used only when pm is set to 'dynamic'
244 | ; Note: Mandatory when pm is set to 'dynamic'
245 | pm.min_spare_servers = 1
246 |
247 | ; The desired maximum number of idle server processes.
248 | ; Note: Used only when pm is set to 'dynamic'
249 | ; Note: Mandatory when pm is set to 'dynamic'
250 | pm.max_spare_servers = 3
251 |
252 | ; The number of seconds after which an idle process will be killed.
253 | ; Note: Used only when pm is set to 'ondemand'
254 | ; Default Value: 10s
255 | ;pm.process_idle_timeout = 10s;
256 |
257 | ; The number of requests each child process should execute before respawning.
258 | ; This can be useful to work around memory leaks in 3rd party libraries. For
259 | ; endless request processing specify '0'. Equivalent to PHP_FCGI_MAX_REQUESTS.
260 | ; Default Value: 0
261 | ;pm.max_requests = 500
262 |
263 | ; The URI to view the FPM status page. If this value is not set, no URI will be
264 | ; recognized as a status page. It shows the following informations:
265 | ; pool - the name of the pool;
266 | ; process manager - static, dynamic or ondemand;
267 | ; start time - the date and time FPM has started;
268 | ; start since - number of seconds since FPM has started;
269 | ; accepted conn - the number of request accepted by the pool;
270 | ; listen queue - the number of request in the queue of pending
271 | ; connections (see backlog in listen(2));
272 | ; max listen queue - the maximum number of requests in the queue
273 | ; of pending connections since FPM has started;
274 | ; listen queue len - the size of the socket queue of pending connections;
275 | ; idle processes - the number of idle processes;
276 | ; active processes - the number of active processes;
277 | ; total processes - the number of idle + active processes;
278 | ; max active processes - the maximum number of active processes since FPM
279 | ; has started;
280 | ; max children reached - number of times, the process limit has been reached,
281 | ; when pm tries to start more children (works only for
282 | ; pm 'dynamic' and 'ondemand');
283 | ; Value are updated in real time.
284 | ; Example output:
285 | ; pool: www
286 | ; process manager: static
287 | ; start time: 01/Jul/2011:17:53:49 +0200
288 | ; start since: 62636
289 | ; accepted conn: 190460
290 | ; listen queue: 0
291 | ; max listen queue: 1
292 | ; listen queue len: 42
293 | ; idle processes: 4
294 | ; active processes: 11
295 | ; total processes: 15
296 | ; max active processes: 12
297 | ; max children reached: 0
298 | ;
299 | ; By default the status page output is formatted as text/plain. Passing either
300 | ; 'html', 'xml' or 'json' in the query string will return the corresponding
301 | ; output syntax. Example:
302 | ; http://www.foo.bar/status
303 | ; http://www.foo.bar/status?json
304 | ; http://www.foo.bar/status?html
305 | ; http://www.foo.bar/status?xml
306 | ;
307 | ; By default the status page only outputs short status. Passing 'full' in the
308 | ; query string will also return status for each pool process.
309 | ; Example:
310 | ; http://www.foo.bar/status?full
311 | ; http://www.foo.bar/status?json&full
312 | ; http://www.foo.bar/status?html&full
313 | ; http://www.foo.bar/status?xml&full
314 | ; The Full status returns for each process:
315 | ; pid - the PID of the process;
316 | ; state - the state of the process (Idle, Running, ...);
317 | ; start time - the date and time the process has started;
318 | ; start since - the number of seconds since the process has started;
319 | ; requests - the number of requests the process has served;
320 | ; request duration - the duration in µs of the requests;
321 | ; request method - the request method (GET, POST, ...);
322 | ; request URI - the request URI with the query string;
323 | ; content length - the content length of the request (only with POST);
324 | ; user - the user (PHP_AUTH_USER) (or '-' if not set);
325 | ; script - the main script called (or '-' if not set);
326 | ; last request cpu - the %cpu the last request consumed
327 | ; it's always 0 if the process is not in Idle state
328 | ; because CPU calculation is done when the request
329 | ; processing has terminated;
330 | ; last request memory - the max amount of memory the last request consumed
331 | ; it's always 0 if the process is not in Idle state
332 | ; because memory calculation is done when the request
333 | ; processing has terminated;
334 | ; If the process is in Idle state, then informations are related to the
335 | ; last request the process has served. Otherwise informations are related to
336 | ; the current request being served.
337 | ; Example output:
338 | ; ************************
339 | ; pid: 31330
340 | ; state: Running
341 | ; start time: 01/Jul/2011:17:53:49 +0200
342 | ; start since: 63087
343 | ; requests: 12808
344 | ; request duration: 1250261
345 | ; request method: GET
346 | ; request URI: /test_mem.php?N=10000
347 | ; content length: 0
348 | ; user: -
349 | ; script: /home/fat/web/docs/php/test_mem.php
350 | ; last request cpu: 0.00
351 | ; last request memory: 0
352 | ;
353 | ; Note: There is a real-time FPM status monitoring sample web page available
354 | ; It's available in: /usr/share/php/fpm/status.html
355 | ;
356 | ; Note: The value must start with a leading slash (/). The value can be
357 | ; anything, but it may not be a good idea to use the .php extension or it
358 | ; may conflict with a real PHP file.
359 | ; Default Value: not set
360 | ;pm.status_path = /status
361 |
362 | ; The ping URI to call the monitoring page of FPM. If this value is not set, no
363 | ; URI will be recognized as a ping page. This could be used to test from outside
364 | ; that FPM is alive and responding, or to
365 | ; - create a graph of FPM availability (rrd or such);
366 | ; - remove a server from a group if it is not responding (load balancing);
367 | ; - trigger alerts for the operating team (24/7).
368 | ; Note: The value must start with a leading slash (/). The value can be
369 | ; anything, but it may not be a good idea to use the .php extension or it
370 | ; may conflict with a real PHP file.
371 | ; Default Value: not set
372 | ;ping.path = /ping
373 |
374 | ; This directive may be used to customize the response of a ping request. The
375 | ; response is formatted as text/plain with a 200 response code.
376 | ; Default Value: pong
377 | ;ping.response = pong
378 |
379 | ; The access log file
380 | ; Default: not set
381 | access.log = /proc/self/fd/2
382 |
383 | ; The access log format.
384 | ; The following syntax is allowed
385 | ; %%: the '%' character
386 | ; %C: %CPU used by the request
387 | ; it can accept the following format:
388 | ; - %{user}C for user CPU only
389 | ; - %{system}C for system CPU only
390 | ; - %{total}C for user + system CPU (default)
391 | ; %d: time taken to serve the request
392 | ; it can accept the following format:
393 | ; - %{seconds}d (default)
394 | ; - %{miliseconds}d
395 | ; - %{mili}d
396 | ; - %{microseconds}d
397 | ; - %{micro}d
398 | ; %e: an environment variable (same as $_ENV or $_SERVER)
399 | ; it must be associated with embraces to specify the name of the env
400 | ; variable. Some exemples:
401 | ; - server specifics like: %{REQUEST_METHOD}e or %{SERVER_PROTOCOL}e
402 | ; - HTTP headers like: %{HTTP_HOST}e or %{HTTP_USER_AGENT}e
403 | ; %f: script filename
404 | ; %l: content-length of the request (for POST request only)
405 | ; %m: request method
406 | ; %M: peak of memory allocated by PHP
407 | ; it can accept the following format:
408 | ; - %{bytes}M (default)
409 | ; - %{kilobytes}M
410 | ; - %{kilo}M
411 | ; - %{megabytes}M
412 | ; - %{mega}M
413 | ; %n: pool name
414 | ; %o: output header
415 | ; it must be associated with embraces to specify the name of the header:
416 | ; - %{Content-Type}o
417 | ; - %{X-Powered-By}o
418 | ; - %{Transfert-Encoding}o
419 | ; - ....
420 | ; %p: PID of the child that serviced the request
421 | ; %P: PID of the parent of the child that serviced the request
422 | ; %q: the query string
423 | ; %Q: the '?' character if query string exists
424 | ; %r: the request URI (without the query string, see %q and %Q)
425 | ; %R: remote IP address
426 | ; %s: status (response code)
427 | ; %t: server time the request was received
428 | ; it can accept a strftime(3) format:
429 | ; %d/%b/%Y:%H:%M:%S %z (default)
430 | ; %T: time the log has been written (the request has finished)
431 | ; it can accept a strftime(3) format:
432 | ; %d/%b/%Y:%H:%M:%S %z (default)
433 | ; %u: remote user
434 | ;
435 | ; Default: "%R - %u %t \"%m %r\" %s"
436 | ;access.format = "%R - %u %t \"%m %r%Q%q\" %s %f %{mili}d %{kilo}M %C%%"
437 |
438 | ; The log file for slow requests
439 | ; Default Value: not set
440 | ; Note: slowlog is mandatory if request_slowlog_timeout is set
441 | ;slowlog = log/$pool.log.slow
442 |
443 | ; The timeout for serving a single request after which a PHP backtrace will be
444 | ; dumped to the 'slowlog' file. A value of '0s' means 'off'.
445 | ; Available units: s(econds)(default), m(inutes), h(ours), or d(ays)
446 | ; Default Value: 0
447 | ;request_slowlog_timeout = 0
448 |
449 | ; The timeout for serving a single request after which the worker process will
450 | ; be killed. This option should be used when the 'max_execution_time' ini option
451 | ; does not stop script execution for some reason. A value of '0' means 'off'.
452 | ; Available units: s(econds)(default), m(inutes), h(ours), or d(ays)
453 | ; Default Value: 0
454 | ;request_terminate_timeout = 0
455 |
456 | ; Set open file descriptor rlimit.
457 | ; Default Value: system defined value
458 | ;rlimit_files = 1024
459 |
460 | ; Set max core size rlimit.
461 | ; Possible Values: 'unlimited' or an integer greater or equal to 0
462 | ; Default Value: system defined value
463 | ;rlimit_core = 0
464 |
465 | ; Chroot to this directory at the start. This value must be defined as an
466 | ; absolute path. When this value is not set, chroot is not used.
467 | ; Note: you can prefix with '$prefix' to chroot to the pool prefix or one
468 | ; of its subdirectories. If the pool prefix is not set, the global prefix
469 | ; will be used instead.
470 | ; Note: chrooting is a great security feature and should be used whenever
471 | ; possible. However, all PHP paths will be relative to the chroot
472 | ; (error_log, sessions.save_path, ...).
473 | ; Default Value: not set
474 | ;chroot =
475 |
476 | ; Chdir to this directory at the start.
477 | ; Note: relative path can be used.
478 | ; Default Value: current directory or / when chroot
479 | ;chdir = /var/www
480 |
481 | ; Redirect worker stdout and stderr into main error log. If not set, stdout and
482 | ; stderr will be redirected to /dev/null according to FastCGI specs.
483 | ; Note: on highloaded environement, this can cause some delay in the page
484 | ; process time (several ms).
485 | ; Default Value: no
486 | catch_workers_output = yes
487 |
488 | ; Clear environment in FPM workers
489 | ; Prevents arbitrary environment variables from reaching FPM worker processes
490 | ; by clearing the environment in workers before env vars specified in this
491 | ; pool configuration are added.
492 | ; Setting to "no" will make all environment variables available to PHP code
493 | ; via getenv(), $_ENV and $_SERVER.
494 | ; Default Value: yes
495 | clear_env = no
496 |
497 | ; Limits the extensions of the main script FPM will allow to parse. This can
498 | ; prevent configuration mistakes on the web server side. You should only limit
499 | ; FPM to .php extensions to prevent malicious users to use other extensions to
500 | ; exectute php code.
501 | ; Note: set an empty value to allow all extensions.
502 | ; Default Value: .php
503 | ;security.limit_extensions = .php .php3 .php4 .php5
504 |
505 | ; Pass environment variables like LD_LIBRARY_PATH. All $VARIABLEs are taken from
506 | ; the current environment.
507 | ; Default Value: clean env
508 | ;env[HOSTNAME] = $HOSTNAME
509 | ;env[PATH] = /usr/local/bin:/usr/bin:/bin
510 | ;env[TMP] = /tmp
511 | ;env[TMPDIR] = /tmp
512 | ;env[TEMP] = /tmp
513 |
514 | ; Additional php.ini defines, specific to this pool of workers. These settings
515 | ; overwrite the values previously defined in the php.ini. The directives are the
516 | ; same as the PHP SAPI:
517 | ; php_value/php_flag - you can set classic ini defines which can
518 | ; be overwritten from PHP call 'ini_set'.
519 | ; php_admin_value/php_admin_flag - these directives won't be overwritten by
520 | ; PHP call 'ini_set'
521 | ; For php_*flag, valid values are on, off, 1, 0, true, false, yes or no.
522 |
523 | ; Defining 'extension' will load the corresponding shared extension from
524 | ; extension_dir. Defining 'disable_functions' or 'disable_classes' will not
525 | ; overwrite previously defined php.ini values, but will append the new value
526 | ; instead.
527 |
528 | ; Note: path INI options can be relative and will be expanded with the prefix
529 | ; (pool, global or /usr)
530 |
531 | ; Default Value: nothing is defined by default except the values in php.ini and
532 | ; specified at startup with the -d argument
533 | ;php_admin_value[sendmail_path] = /usr/sbin/sendmail -t -i -f www@my.domain.com
534 | php_flag[display_errors] = off
535 | ;php_admin_value[error_log] = /var/log/fpm-php.www.log
536 | ;php_admin_flag[log_errors] = on
537 | ;php_admin_value[memory_limit] = 32M
538 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM alpine:3.2
2 |
3 | RUN apk add --update \
4 | php-fpm \
5 | php-mcrypt \
6 | php-curl \
7 | php-openssl \
8 | php-phar \
9 | php-ctype \
10 | php-json \
11 | curl \
12 | git \
13 | php-dom \
14 | alpine-sdk \
15 | php-dev \
16 | autoconf \
17 | openssl-dev \
18 | php-pdo \
19 | php-pdo_pgsql \
20 | php-pdo_odbc \
21 | php-pdo_mysql \
22 | php-pdo_sqlite \
23 | php-opcache && \
24 | sed -i 's/\;date\.timezone\ \=/date\.timezone\ \=\ Europe\/Berlin/g' /etc/php/php.ini && \
25 | curl -sS https://getcomposer.org/installer | php && \
26 | mv composer.phar /usr/local/bin/composer && \
27 | cd /tmp && git clone https://github.com/phpredis/phpredis.git && cd /tmp/phpredis && \
28 | git checkout 2.2.7 && phpize && \
29 | ./configure && \
30 | make && \
31 | make install && \
32 | echo "extension=redis.so" >> /etc/php/conf.d/redis.ini && \
33 | rm -rf /tmp/* && \
34 | apk del --purge openssl-dev autoconf php-dev alpine-sdk && \
35 | rm -rf /var/cache/apk/*
36 | COPY Docker/php-fpm.conf /etc/php/php-fpm.conf
37 | ADD . /var/www/
38 | WORKDIR /var/www
39 | RUN chmod +x Docker/entrypoint.sh
40 | CMD ["Docker/entrypoint.sh"]
41 | EXPOSE 9000
42 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Bogdan Popescu
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/Procfile:
--------------------------------------------------------------------------------
1 | web: vendor/bin/heroku-php-nginx -C nginx.conf public/
2 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Dash Annotations Server
2 |
3 | Follow these instructions if you want to set up your own annotation server for [Dash](https://kapeli.com/dash).
4 |
5 | ## Installation
6 |
7 | * Install [Lumen](http://lumen.laravel.com/docs/installation)
8 | * Add a MySQL database called "annotations"
9 | * Clone this repo over your Lumen install
10 | * Rename the `.env.example` file to `.env` and edit it
11 | * Run `composer install`
12 | * Install Python and [Pygments](http://pygments.org/) (used for syntax highlighting)
13 | * Make sure `/bin/pygmentize` exists. If it doesn't, add a link between `/bin/pygmentize` to wherever you installed Pygments
14 | * Run `php artisan migrate` and type `Y` to confirm you want to do it in production
15 | * Open `http://{your_server}/users/logout` in your browser and check if you get a JSON response that says you're not logged in
16 | * Let Dash know about your server by running this command in Terminal:
17 |
18 | ```bash
19 | # Repeat on every Mac that will connect to your server:
20 | defaults write com.kapeli.dashdoc AnnotationsCustomServer "http(s)://{your_server}"
21 |
22 | # To go back to the default server:
23 | defaults delete com.kapeli.dashdoc AnnotationsCustomServer
24 | ```
25 |
26 | * If you encounter any issues, [let me know](https://github.com/Kapeli/Dash-Annotations/issues/new)!
27 |
28 | ### Docker
29 |
30 | * Clone this repo
31 | * Build the image: `docker-compose build`
32 | * Generate your [GitHub Token](https://github.com/settings/tokens) and add it to `docker-compose.yml`
33 | * Set your `APP_KEY` in `docker-compose.yml`
34 | * Start the service: `docker-compose up -d`
35 | * Add `ProxyNginx.conf` to your nginx sites and edit your `server_name`
36 | * Open `http://dash.{your_server}/users/logout` in your browser and check if you get a JSON response that says you're not logged in
37 | * Let Dash know about your server by running this command in Terminal:
38 |
39 | ```bash
40 | # Repeat on every Mac that will connect to your server:
41 | defaults write com.kapeli.dashdoc AnnotationsCustomServer "http(s)://dash.{your_server}"
42 |
43 | # To go back to the default server:
44 | defaults delete com.kapeli.dashdoc AnnotationsCustomServer
45 | ```
46 |
47 | * If you encounter any issues, [let me know](https://github.com/Kapeli/Dash-Annotations/issues/new)!
48 |
49 |
50 | ### Dokku
51 | > https://github.com/dokku-alt/dokku-alt
52 |
53 | * Clone this repo
54 | * Create remote for dokku: `git remote add dokku dokku@{your_server}:dash`
55 | * Create the app: `ssh -t dokku@{your_server} create dash`
56 | * Create the database: `ssh -t dokku@{your_server} mariadb:create dash-db`
57 | * Link database: `ssh -t dokku@{your_server} mariadb:link dash dash-db`
58 | * Get the database credentials: `ssh -t dokku@{your_server} mariadb:info dash dash-db`
59 | * Create environmental variables:
60 | ```
61 | ssh -t dokku@{your_server} config:set dash \
62 | APP_ENV=production \
63 | APP_FALLBACK_LOCAL=en \
64 | APP_KEY=SomeRandomKey! \
65 | APP_LOCALE=en \
66 | CACHE_DRIVER=file \
67 | DB_CONNECTION=mysql \
68 | DB_DATABASE=dash-db \
69 | DB_HOST=mariadb \
70 | DB_PASSWORD=YourPassword \
71 | DB_USERNAME=dash \
72 | QUEUE_DRIVER=file \
73 | SESSION_DRIVER=file
74 | ```
75 |
76 | * Push to dokku: `git push dokku dokku:master`
77 | * Get your server's URL: `ssh -t dokku@{your_server} url dash`
78 | * Open `http://dash.{your_server}/users/logout` in your browser and check if you get a JSON response that says you're not logged in
79 | * Let Dash know about your server by running this command in Terminal:
80 |
81 | ```bash
82 | # Repeat on every Mac that will connect to your server:
83 | defaults write com.kapeli.dashdoc AnnotationsCustomServer "http(s)://dash.{your_server}"
84 |
85 | # To go back to the default server:
86 | defaults delete com.kapeli.dashdoc AnnotationsCustomServer
87 | ```
88 |
89 | * If you encounter any issues, [let me know](https://github.com/Kapeli/Dash-Annotations/issues/new)!
90 |
--------------------------------------------------------------------------------
/app/Console/Commands/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kapeli/Dash-Annotations/9c575f555f75ef94776ccf07995a81b210fac280/app/Console/Commands/.gitkeep
--------------------------------------------------------------------------------
/app/Console/Kernel.php:
--------------------------------------------------------------------------------
1 | {'status'};
60 | if($app_store_response_status == 0 && strcmp(trim($app_store_response_map->receipt->bundle_id), "com.kapeli.dashdoc") == 0)
61 | {
62 | foreach($app_store_response_map->receipt->in_app as $in_app)
63 | {
64 | if(strcmp(trim($in_app->product_id), "FullVersion") == 0)
65 | {
66 | return true;
67 | }
68 | }
69 | }
70 | }
71 | }
72 | return false;
73 | }
74 | }
--------------------------------------------------------------------------------
/app/Http/Controllers/EntriesController.php:
--------------------------------------------------------------------------------
1 | find_in_db();
22 | if($identifier)
23 | {
24 | $public_entries = NULL;
25 | $own_entries = NULL;
26 | $team_entries = NULL;
27 |
28 | $user = Auth::user();
29 | if($user)
30 | {
31 | $team_ids = array();
32 | foreach($user->teams()->get() as $team)
33 | {
34 | $team_ids[] = $team->id;
35 | }
36 |
37 | $public_entries = $identifier->entries()->where('public', '=', 1)
38 | ->where('user_id', '!=', $user->id)
39 | ->where('removed_from_public', '!=', 1)
40 | ->where('score', '>', $minimum_public_score)->get();
41 | $own_entries = $identifier->entries()->where('user_id', '=', $user->id)->get();
42 | if(smart_count($team_ids))
43 | {
44 | $team_entries = $identifier->entries()->whereHas('teams', function($query) use ($user, $team_ids)
45 | {
46 | $query->where('user_id', '!=', $user->id)
47 | ->whereIn('team_id', $team_ids)
48 | ->where('removed_from_team', '=', 0);
49 | })->get();
50 | if($team_entries && $public_entries)
51 | {
52 | $public_entries = $public_entries->filter(function($public_entry) use ($team_entries)
53 | {
54 | foreach($team_entries as $team_entry)
55 | {
56 | if($public_entry->id == $team_entry->id)
57 | {
58 | return false;
59 | }
60 | }
61 | return true;
62 | })->values();
63 | }
64 | }
65 | }
66 | else
67 | {
68 | $public_entries = $identifier->entries()->where('public', '=', 1)
69 | ->where('score', '>', $minimum_public_score)
70 | ->where('removed_from_public', '!=', 1)
71 | ->get();
72 | }
73 |
74 | $response = ["status" => "success"];
75 | if(smart_count($public_entries))
76 | {
77 | $response["public_entries"] = $public_entries;
78 | }
79 | if(smart_count($own_entries))
80 | {
81 | $response["own_entries"] = $own_entries;
82 | }
83 | if(smart_count($team_entries))
84 | {
85 | $response["team_entries"] = $team_entries;
86 | }
87 | return json_encode($response);
88 | }
89 | return json_encode(["status" => "success"]);
90 | }
91 |
92 | public function save()
93 | {
94 | if(Auth::check())
95 | {
96 | $title = Request::input('title');
97 | $body = Request::input('body');
98 | $public = Request::input('public');
99 | $type = Request::input('type');
100 | $teams = Request::input('teams');
101 | $license = Request::input('license');
102 | $identifier_dict = Request::input('identifier');
103 | $anchor = Request::input('anchor');
104 | $entry_id = Request::input('entry_id');
105 | $user = Auth::user();
106 |
107 | if($title !== '' && $body !== '' && $type !== '' && !empty($identifier_dict) && $anchor !== '')
108 | {
109 | $db_license = NULL;
110 | if($public)
111 | {
112 | if(isset($_ENV['AUTH_LICENSES']) && $_ENV['AUTH_LICENSES'])
113 | {
114 | if(empty($license))
115 | {
116 | return json_encode(['status' => 'error', 'message' => 'Only paid users can create public annotations']);
117 | }
118 |
119 | $json_license = json_encode($license);
120 | $db_license = License::where('license', '=', $json_license)->first();
121 | if($db_license)
122 | {
123 | if($db_license->banned_from_public)
124 | {
125 | if(isset($license['is_beta']) && $license['is_beta'])
126 | {
127 | return json_encode(['status' => 'error', 'message' => "Beta users can't make public annotations"]);
128 | }
129 | else if(isset($license['is_promo']) && $license['is_promo'])
130 | {
131 | return json_encode(['status' => 'error', 'message' => $license['promo_name']." users can't make public annotations"]);
132 | }
133 | return json_encode(['status' => 'error', 'message' => 'You are banned from making public annotations']);
134 | }
135 | }
136 | else
137 | {
138 | if(isset($license['is_beta']) && $license['is_beta'])
139 | {
140 | // skip check for beta users
141 | }
142 | else if(isset($license['is_promo']) && $license['is_promo'])
143 | {
144 | // skip check for promo users
145 | }
146 | else if(isset($license['is_subscribed']) && $license['is_subscribed'])
147 | {
148 | // skip check for subscribed users
149 | }
150 | else if(isset($license['is_app_store']) && $license['is_app_store'])
151 | {
152 | if(!DashLicenseUtil::check_itunes_receipt($license))
153 | {
154 | return json_encode(['status' => 'error', 'message' => 'Invalid license. Public annotation not allowed']);
155 | }
156 | }
157 | else
158 | {
159 | if(!DashLicenseUtil::check_license($license))
160 | {
161 | return json_encode(['status' => 'error', 'message' => 'Invalid license. Public annotation not allowed']);
162 | }
163 | }
164 |
165 | $db_license = new License;
166 | $db_license->license = $json_license;
167 | $db_license->save();
168 | }
169 | }
170 | }
171 |
172 | $identifier = Identifier::IdentifierFromDictionary($identifier_dict);
173 | $db_identifier = $identifier->find_in_db();
174 | if(!$db_identifier)
175 | {
176 | $identifier->save();
177 | $db_identifier = $identifier;
178 | }
179 |
180 | if($public && $db_identifier->banned_from_public)
181 | {
182 | return json_encode(['status' => 'error', 'message' => 'Public annotations are not allowed on this page']);
183 | }
184 |
185 | $entry = ($entry_id) ? Entry::where('id', '=', $entry_id)->first() : new Entry;
186 | if($entry_id && (!$entry || $entry->user_id != $user->id))
187 | {
188 | return json_encode(['status' => 'error', 'message' => 'Error. Logout and try again']);
189 | }
190 | $entry->title = $title;
191 | $entry->body = $body;
192 |
193 | try {
194 | $body = MarkdownExtra::defaultTransform($body);
195 | } catch (\RuntimeException $e) {
196 | $message = $e->getMessage();
197 | $start = strpos($message, 'no lexer for alias \'');
198 | if($start !== FALSE)
199 | {
200 | $start += 20;
201 | $end = strpos($message, '\'', $start);
202 | if($end !== FALSE)
203 | {
204 | $lexer = substr($message, $start, $end-$start);
205 | return json_encode(['status' => 'error', 'message' => 'Unknown syntax highlighting: '.$lexer]);
206 | }
207 | }
208 | throw $e;
209 | }
210 | $html_safe = new HTML_Safe();
211 | $html_safe->protocolFiltering = 'black';
212 | $body = $html_safe->parse($body);
213 | $body = str_replace('#dashInternal', '#', $body);
214 | $entry->body_rendered = $body;
215 |
216 | $entry->public = $public;
217 | $entry->type = $type;
218 | $entry->anchor = $anchor;
219 | $entry->user_id = $user->id;
220 | $entry->identifier_id = $db_identifier->id;
221 | if($db_license)
222 | {
223 | $entry->license_id = $db_license->id;
224 | }
225 | if(!$entry_id)
226 | {
227 | $entry->score = 1;
228 | }
229 | $entry->save();
230 | if(!$entry_id)
231 | {
232 | $vote = new Vote;
233 | $vote->type = 1;
234 | $vote->user_id = $user->id;
235 | $vote->entry_id = $entry->id;
236 | $vote->save();
237 | }
238 | $db_teams = $entry->teams();
239 | $already_assigned = array();
240 | foreach($db_teams->get() as $team)
241 | {
242 | if(!in_arrayi($team->name, $teams))
243 | {
244 | $db_teams->detach($team->id);
245 | }
246 | else
247 | {
248 | $already_assigned[] = $team->name;
249 | }
250 | }
251 | foreach($teams as $team)
252 | {
253 | if(!in_arrayi($team, $already_assigned))
254 | {
255 | $db_team = Team::where('name', '=', $team)->first();
256 | if($db_team && $db_team->users()->where('user_id', '=', $user->id)->first())
257 | {
258 | $db_team->entries()->attach($entry->id);
259 | }
260 | }
261 | }
262 | return json_encode(['status' => 'success', 'entry' => $entry]);
263 | }
264 | return json_encode(['status' => 'error', 'message' => 'Oops. Unknown error']);
265 | }
266 | return json_encode(['status' => 'error', 'message' => 'Error. Logout and try again']);
267 | }
268 |
269 | public function delete()
270 | {
271 | if(Auth::check())
272 | {
273 | $entry_id = Request::input('entry_id');
274 | $entry = Entry::where('id', '=', $entry_id)->first();
275 | $user = Auth::user();
276 | if($entry && $entry->user_id == $user->id)
277 | {
278 | $entry->teams()->detach();
279 | Vote::where('entry_id', '=', $entry_id)->delete();
280 | $entry->delete();
281 | return json_encode(['status' => 'success']);
282 | }
283 | }
284 | return json_encode(['status' => 'error', 'message' => 'Error. Logout and try again']);
285 | }
286 |
287 | public function remove_from_public()
288 | {
289 | if(Auth::check())
290 | {
291 | $user = Auth::user();
292 | if($user->moderator)
293 | {
294 | $entry_id = Request::input('entry_id');
295 | $entry = Entry::where('id', '=', $entry_id)->first();
296 | if($entry)
297 | {
298 | $entry->removed_from_public = true;
299 | $entry->save();
300 | return json_encode(['status' => 'success']);
301 | }
302 | }
303 | }
304 | return json_encode(['status' => 'error', 'message' => 'Error. Logout and try again']);
305 | }
306 |
307 | public function remove_from_teams()
308 | {
309 | if(Auth::check())
310 | {
311 | $user = Auth::user();
312 | $entry_id = Request::input('entry_id');
313 | $entry = Entry::where('id', '=', $entry_id)->first();
314 | if($entry)
315 | {
316 | $entry_teams = $entry->teams()->get();
317 | foreach($entry_teams as $team)
318 | {
319 | $check = $team->users()->where('user_id', '=', $user->id)->first();
320 | if($check)
321 | {
322 | $role = $check->pivot->role;
323 | if($role && ($role == 'owner' || $role == 'moderator'))
324 | {
325 | $team->pivot->removed_from_team = true;
326 | $team->pivot->save();
327 | }
328 | }
329 | }
330 | return json_encode(['status' => 'success']);
331 | }
332 | }
333 | return json_encode(['status' => 'error', 'message' => 'Error. Logout and try again']);
334 | }
335 |
336 | public function vote()
337 | {
338 | if(Auth::check())
339 | {
340 | $entry_id = Request::input('entry_id');
341 | $vote_type = Request::input('vote_type');
342 | if($vote_type > 1 || $vote_type < -1)
343 | {
344 | return json_encode(['status' => 'error', 'message' => 'Error. Logout and try again']);
345 | }
346 | $entry = Entry::where('id', '=', $entry_id)->first();
347 | if($entry)
348 | {
349 | $entry_teams = $entry->teams()->get();
350 | $user = Auth::user();
351 | if(!$entry->public && $entry->user_id != $user->id)
352 | {
353 | $found = false;
354 | foreach($entry_teams as $team)
355 | {
356 | $check = $team->users()->where('user_id', '=', $user->id)->first();
357 | if($check)
358 | {
359 | $found = true;
360 | break;
361 | }
362 | }
363 | if(!$found)
364 | {
365 | return json_encode(['status' => 'error', 'message' => 'Error. Logout and try again']);
366 | }
367 | }
368 | $vote = Vote::where('entry_id', '=', $entry_id)->where('user_id', '=', $user->id)->first();
369 | if($vote)
370 | {
371 | $entry->score -= $vote->type;
372 | if($vote_type == 0)
373 | {
374 | $entry->save();
375 | $vote->delete();
376 | return json_encode(['status' => 'success']);
377 | }
378 | }
379 | else if($vote_type == 0)
380 | {
381 | return json_encode(['status' => 'error', 'message' => 'Error. Logout and try again']);
382 | }
383 | $vote = ($vote) ? $vote : new Vote;
384 | $vote->entry_id = $entry_id;
385 | $vote->user_id = $user->id;
386 | $vote->type = $vote_type;
387 | $entry->score += $vote_type;
388 | $entry->save();
389 | $vote->save();
390 | return json_encode(['status' => 'success']);
391 | }
392 | }
393 | return json_encode(['status' => 'error', 'message' => 'Error. Logout and try again']);
394 | }
395 |
396 | public function get()
397 | {
398 | $entry_id = Request::input('entry_id');
399 | $entry = Entry::where('id', '=', $entry_id)->first();
400 | if($entry)
401 | {
402 | $my_teams = array();
403 | $global_moderator = false;
404 | $team_moderator = false;
405 | $entry_user = $entry->user()->first();
406 | $user = NULL;
407 | if(!Auth::check())
408 | {
409 | if(!$entry->public)
410 | {
411 | return json_encode(['status' => 'error', 'message' => 'Error. Logout and try again']);
412 | }
413 | }
414 | else
415 | {
416 | $entry_teams = $entry->teams()->get();
417 | $user = Auth::user();
418 | foreach($entry_teams as $team)
419 | {
420 | if($entry->user_id == $user->id || !$team->pivot->removed_from_team)
421 | {
422 | $check = $team->users()->where('user_id', '=', $user->id)->first();
423 | if($check)
424 | {
425 | $role = $check->pivot->role;
426 | $my_teams[] = ["name" => $team->name, "role" => $role];
427 | if($role && ($role == 'owner' || $role == 'moderator'))
428 | {
429 | $team_moderator = true;
430 | }
431 | }
432 | }
433 | }
434 | if(!$entry->public && !smart_count($my_teams) && $entry->user_id != $user->id)
435 | {
436 | return json_encode(['status' => 'error', 'message' => 'Error. Logout and try again']);
437 | }
438 | if($entry->public && $user->moderator)
439 | {
440 | $global_moderator = true;
441 | }
442 | }
443 | $body_rendered = $entry->body_rendered;
444 | $body = '
445 |
446 |
447 |