wip
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@ -65,4 +65,5 @@ vendor/canary/logs/*
|
|||||||
components/*
|
components/*
|
||||||
mailhog.log
|
mailhog.log
|
||||||
uploads/*
|
uploads/*
|
||||||
images/qr-codes/
|
vendor/*
|
||||||
|
images/qr-codes/*
|
||||||
|
@ -1,74 +0,0 @@
|
|||||||
stages:
|
|
||||||
- prepare
|
|
||||||
- build
|
|
||||||
- test
|
|
||||||
- update
|
|
||||||
- deploy
|
|
||||||
|
|
||||||
variables:
|
|
||||||
TIMEZONE: "America/New_York" # For the system in general
|
|
||||||
DATE_TIMEZONE: ${TIMEZONE} # For PHP
|
|
||||||
|
|
||||||
GIT_DEPTH: 1
|
|
||||||
GITLAB_API_URL: ${CI_API_V4_URL}
|
|
||||||
TARGET_BRANCH: ${CI_COMMIT_REF_NAME} # This is the branch chosen in the `Pipeline Schedule`
|
|
||||||
TARGET_REMOTE: "https://${GITLAB_USERNAME}:${GITLAB_ACCESS_TOKEN}@${CI_SERVER_HOST}/${CI_PROJECT_NAMESPACE}/${CI_PROJECT_NAME}.git"
|
|
||||||
|
|
||||||
# These could/should be overridden in an extending job:
|
|
||||||
UPDATE_BRANCH_PREFIX: "update_PHP_deps_" # Used for the update branch name, it will be followed by the datetime
|
|
||||||
GIT_USER: "DependBot" # Used for the update commit
|
|
||||||
GIT_EMAIL: "webmaster@thetempusproject.com" # Used for the update commit
|
|
||||||
GITLAB_USERNAME: "root" # Used for pushing the new branch and opening the MR
|
|
||||||
GITLAB_ACCESS_TOKEN: "glpat-PKEmivGtBfbz4DVPdhzk" # Used for pushing the new branch and opening the MR
|
|
||||||
MERGE_IF_SUCCESSFUL: "true" # Set to true, to merge automatically if the pipeline succeeds
|
|
||||||
SECONDS_BETWEEN_POOLING: 10 # Nbr of seconds between checking if the MR pipeline is successful, so then it will merge
|
|
||||||
JOB_GIT_FLAGS: ""
|
|
||||||
JOB_CURL_FLAGS: ""
|
|
||||||
JOB_COMPOSER_FLAGS: ""
|
|
||||||
|
|
||||||
composer_update:
|
|
||||||
stage: update
|
|
||||||
rules:
|
|
||||||
- if: '$CI_COMMIT_BRANCH == "main"'
|
|
||||||
image: composer:latest
|
|
||||||
interruptible: true # allows to stop the job if a newer pipeline starts, saving resources and allowing new jobs to start because job concurrency is limited
|
|
||||||
script:
|
|
||||||
- git ${JOB_GIT_FLAGS} fetch origin ${TARGET_BRANCH}
|
|
||||||
- git ${JOB_GIT_FLAGS} checkout ${TARGET_BRANCH}
|
|
||||||
- git reset --hard origin/main
|
|
||||||
- git pull --allow-unrelated-histories
|
|
||||||
- export DATE_TIME="$(date '+%Y%m%d%H%M%S')"
|
|
||||||
- export MR_BRANCH="${UPDATE_BRANCH_PREFIX}${DATE_TIME}"
|
|
||||||
- git ${JOB_GIT_FLAGS} checkout -b "${MR_BRANCH}"
|
|
||||||
- composer update ${JOB_COMPOSER_FLAGS}
|
|
||||||
- if [ "$(git diff)" == "" ]; then echo "No updates needed!"; exit 0; fi
|
|
||||||
- export TITLE="Update PHP dependencies [${DATE_TIME}]"
|
|
||||||
- git ${JOB_GIT_FLAGS} commit -a -m "${TITLE}"
|
|
||||||
- git ${JOB_GIT_FLAGS} push "${TARGET_REMOTE}" "${MR_BRANCH}"
|
|
||||||
artifacts:
|
|
||||||
paths:
|
|
||||||
- vendor/
|
|
||||||
cache:
|
|
||||||
key: ${CI_COMMIT_REF_SLUG}
|
|
||||||
paths:
|
|
||||||
- vendor/
|
|
||||||
|
|
||||||
prepare:
|
|
||||||
stage: prepare
|
|
||||||
script:
|
|
||||||
- echo "Preparing environment..."
|
|
||||||
|
|
||||||
build:
|
|
||||||
stage: build
|
|
||||||
script:
|
|
||||||
- echo "Building the project..."
|
|
||||||
|
|
||||||
test:
|
|
||||||
stage: test
|
|
||||||
script:
|
|
||||||
- echo "Running tests..."
|
|
||||||
|
|
||||||
deploy:
|
|
||||||
stage: deploy
|
|
||||||
script:
|
|
||||||
- echo "Deploying the project..."
|
|
@ -1,127 +0,0 @@
|
|||||||
<?php
|
|
||||||
$finder = PhpCsFixer\Finder::create()
|
|
||||||
//->exclude('somedir')
|
|
||||||
//->notPath('src/Symfony/Component/Translation/Tests/fixtures/resources.php'
|
|
||||||
->in(__DIR__)
|
|
||||||
;
|
|
||||||
|
|
||||||
$config = new \PhpCsFixer\Config();
|
|
||||||
return $config->setRules([
|
|
||||||
'@PSR2' => true,
|
|
||||||
'array_indentation' => true,
|
|
||||||
'array_syntax' => ['syntax' => 'short'],
|
|
||||||
'combine_consecutive_unsets' => true,
|
|
||||||
'class_attributes_separation' => ['elements' => ['method' => 'one',]],
|
|
||||||
'multiline_whitespace_before_semicolons' => false,
|
|
||||||
'single_quote' => true,
|
|
||||||
'strict_param' => false,
|
|
||||||
'binary_operator_spaces' => [
|
|
||||||
'operators' => [
|
|
||||||
// '=>' => 'align',
|
|
||||||
// '=' => 'align'
|
|
||||||
]
|
|
||||||
],
|
|
||||||
// 'blank_line_after_opening_tag' => true,
|
|
||||||
// 'blank_line_before_statement' => true,
|
|
||||||
'braces' => [
|
|
||||||
'allow_single_line_closure' => true,
|
|
||||||
'position_after_functions_and_oop_constructs' => 'same'
|
|
||||||
],
|
|
||||||
// 'cast_spaces' => true,
|
|
||||||
// 'class_definition' => array('singleLine' => true),
|
|
||||||
'concat_space' => ['spacing' => 'one'],
|
|
||||||
// 'declare_equal_normalize' => true,
|
|
||||||
// 'function_typehint_space' => true,
|
|
||||||
// 'single_line_comment_style' => ['comment_types' => ['hash']],
|
|
||||||
// 'include' => true,
|
|
||||||
// 'lowercase_cast' => true,
|
|
||||||
// 'native_function_casing' => true,
|
|
||||||
// 'new_with_braces' => true,
|
|
||||||
// 'no_blank_lines_after_class_opening' => true,
|
|
||||||
// 'no_blank_lines_after_phpdoc' => true,
|
|
||||||
// 'no_blank_lines_before_namespace' => true,
|
|
||||||
'no_empty_comment' => true,
|
|
||||||
'no_empty_phpdoc' => true,
|
|
||||||
// 'no_empty_statement' => true,
|
|
||||||
'no_extra_blank_lines' => [
|
|
||||||
'tokens' => [
|
|
||||||
// 'curly_brace_block',
|
|
||||||
// 'extra',
|
|
||||||
// 'parenthesis_brace_block',
|
|
||||||
// 'square_brace_block',
|
|
||||||
// 'throw',
|
|
||||||
// 'use',
|
|
||||||
]
|
|
||||||
],
|
|
||||||
'no_leading_import_slash' => true,
|
|
||||||
'no_leading_namespace_whitespace' => true,
|
|
||||||
'no_mixed_echo_print' => ['use' => 'echo'],
|
|
||||||
'no_multiline_whitespace_around_double_arrow' => true,
|
|
||||||
'no_short_bool_cast' => true,
|
|
||||||
'no_singleline_whitespace_before_semicolons' => true,
|
|
||||||
'no_spaces_around_offset' => ['positions' => ['outside']],
|
|
||||||
'no_trailing_comma_in_singleline' => ['elements' => ['arguments', 'array_destructuring', 'array', 'group_import']],
|
|
||||||
|
|
||||||
'spaces_inside_parentheses' => ['space' => 'single'],
|
|
||||||
// 'no_spaces_inside_parenthesis' => false,
|
|
||||||
'control_structure_braces' => true,
|
|
||||||
|
|
||||||
'curly_braces_position' => [
|
|
||||||
'control_structures_opening_brace' => 'same_line',
|
|
||||||
'functions_opening_brace' => 'same_line',
|
|
||||||
'classes_opening_brace' => 'same_line',
|
|
||||||
],
|
|
||||||
|
|
||||||
|
|
||||||
// need to add space after array declaration
|
|
||||||
// need to put each element on a line by itself when an array is multi line
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
'no_unneeded_control_parentheses' => true,
|
|
||||||
'no_unused_imports' => true,
|
|
||||||
'no_whitespace_before_comma_in_array' => true,
|
|
||||||
'no_whitespace_in_blank_line' => true,
|
|
||||||
'normalize_index_brace' => true,
|
|
||||||
// 'object_operator_without_whitespace' => true,
|
|
||||||
// 'php_unit_fqcn_annotation' => true,
|
|
||||||
// 'phpdoc_align' => true,
|
|
||||||
// 'phpdoc_annotation_without_dot' => true,
|
|
||||||
// 'phpdoc_indent' => true,
|
|
||||||
// 'phpdoc_inline_tag' => true,
|
|
||||||
// 'phpdoc_no_access' => true,
|
|
||||||
// 'phpdoc_no_alias_tag' => true,
|
|
||||||
// 'phpdoc_no_empty_return' => true,
|
|
||||||
// 'phpdoc_no_package' => true,
|
|
||||||
// 'phpdoc_no_useless_inheritdoc' => true,
|
|
||||||
// 'phpdoc_return_self_reference' => true,
|
|
||||||
// 'phpdoc_scalar' => true,
|
|
||||||
// 'phpdoc_separation' => true,
|
|
||||||
// 'phpdoc_single_line_var_spacing' => true,
|
|
||||||
// 'phpdoc_summary' => true,
|
|
||||||
// 'phpdoc_to_comment' => true,
|
|
||||||
// 'phpdoc_trim' => true,
|
|
||||||
// 'phpdoc_types' => true,
|
|
||||||
'phpdoc_var_without_name' => false,
|
|
||||||
'increment_style' => ['style' => 'post'],
|
|
||||||
'return_type_declaration' => true,
|
|
||||||
// 'self_accessor' => true, // risky
|
|
||||||
'short_scalar_cast' => true,
|
|
||||||
'single_class_element_per_statement' => true,
|
|
||||||
'standardize_not_equals' => true,
|
|
||||||
'ternary_operator_spaces' => true,
|
|
||||||
'trailing_comma_in_multiline' => true,
|
|
||||||
'trim_array_spaces' => false,
|
|
||||||
'unary_operator_spaces' => true,
|
|
||||||
'whitespace_after_comma_in_array' => true,
|
|
||||||
'single_blank_line_at_eof' => true
|
|
||||||
])
|
|
||||||
->setLineEnding("\n")
|
|
||||||
;
|
|
@ -1,47 +0,0 @@
|
|||||||
# Contributor Covenant Code of Conduct
|
|
||||||
|
|
||||||
## Our Pledge
|
|
||||||
|
|
||||||
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
|
|
||||||
|
|
||||||
## Our Standards
|
|
||||||
|
|
||||||
|
|
||||||
Examples of behavior that contributes to creating a positive environment include:
|
|
||||||
|
|
||||||
* Using welcoming and inclusive language
|
|
||||||
* Being respectful of differing viewpoints and experiences
|
|
||||||
* Gracefully accepting constructive criticism
|
|
||||||
* Focusing on what is best for the community
|
|
||||||
* Showing empathy towards other community members
|
|
||||||
|
|
||||||
Examples of unacceptable behavior by participants include:
|
|
||||||
|
|
||||||
* The use of sexualized language or imagery and unwelcome sexual attention or advances
|
|
||||||
* Trolling, insulting/derogatory comments, and personal or political attacks
|
|
||||||
* Public or private harassment
|
|
||||||
* Publishing others' private information, such as a physical or electronic address, without explicit permission
|
|
||||||
* Other conduct which could reasonably be considered inappropriate in a professional setting
|
|
||||||
|
|
||||||
## Our Responsibilities
|
|
||||||
|
|
||||||
Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
|
|
||||||
|
|
||||||
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
|
|
||||||
|
|
||||||
## Scope
|
|
||||||
|
|
||||||
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
|
|
||||||
|
|
||||||
## Enforcement
|
|
||||||
|
|
||||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at webmaster@thetempusproject.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
|
|
||||||
|
|
||||||
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
|
|
||||||
|
|
||||||
## Attribution
|
|
||||||
|
|
||||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
|
|
||||||
|
|
||||||
[homepage]: http://contributor-covenant.org
|
|
||||||
[version]: http://contributor-covenant.org/version/1/4/
|
|
135
CONTRIBUTING.md
135
CONTRIBUTING.md
@ -1,135 +0,0 @@
|
|||||||
# Contribution Guidelines for TheTempusProject
|
|
||||||
Contributing to TheTempusProject is completely voluntary and should follow all of the guidelines listed here in order to ensure the highest probability of acceptance. It is highly recommended to use a php linter to automate more of this process. The project is maintained on github and all contributions need to be submitted via pull request to their specific repository under the `dev` branch. In order to contribute, simply follow the instructions for [creating a pull request](#creating-a-pull-request) below.
|
|
||||||
|
|
||||||
## Pull Request Requirements
|
|
||||||
- All revisions must follow TTP naming conventions (see [Naming Conventions](#naming-conventions) Section)
|
|
||||||
- Include a clear and concise explanation of the features or changes included in your revision listed by file.
|
|
||||||
- All code must follow [PSR 2](http://www.php-fig.org/psr/psr-2/) standards
|
|
||||||
- prefer the use of [] for arrays over array()
|
|
||||||
- All functions must be documented with the exception of controller methods (see [Documentation](#documentation) Section)
|
|
||||||
- Controller methods may be doc-blocked when necessary for clarity (see [Documentation](#documentation) Section)
|
|
||||||
- All new Classes must include a class level doc-block (see [Documentation](#documentation) Section)
|
|
||||||
- Any new dependencies will have a longer validation process and should be accompanied by the required information (see [Dependencies](#dependencies) Section)
|
|
||||||
|
|
||||||
## Naming Conventions
|
|
||||||
- File names are to be lower case
|
|
||||||
- All class names must be upper case
|
|
||||||
- Any data being stored as a file must be saved in the app directory (with the exception of config which should be stored under config/)
|
|
||||||
- Controllers must have a constructor and destructor using the constructor and destructor methods found in resources/
|
|
||||||
- Views must be named using lowerCamelCase
|
|
||||||
|
|
||||||
## Dependencies
|
|
||||||
Whenever a dependency is updated or added, pull requests must include a section that answers the following questions.
|
|
||||||
- Why is this dependency required
|
|
||||||
- Could this be reasonably accomplished within the app by implementing new features in a later version? explain.
|
|
||||||
- What is the latest stable version that can be used
|
|
||||||
- What features are absolutely necessary for your feature or modification to work
|
|
||||||
|
|
||||||
## Documentation
|
|
||||||
### Classes
|
|
||||||
|
|
||||||
New classes must be prefaced with a doc-block following this style:
|
|
||||||
```
|
|
||||||
/**
|
|
||||||
* app/controllers/admin/admin.php
|
|
||||||
*
|
|
||||||
* This is the admin controller.
|
|
||||||
*
|
|
||||||
* @version 5.0.1
|
|
||||||
* @author Joey Kimsey <Joey@thetempusproject.com>
|
|
||||||
* @link https://TheTempusProject.com
|
|
||||||
* @license https://opensource.org/licenses/MIT [MIT LICENSE]
|
|
||||||
*/
|
|
||||||
```
|
|
||||||
|
|
||||||
From top to bottom:
|
|
||||||
- Filename on the second line
|
|
||||||
- A description for the file
|
|
||||||
- The TTP version this file was built for
|
|
||||||
`@version 1.0`
|
|
||||||
- The Authors name or alias and email
|
|
||||||
`@author first last <email@link.com>`
|
|
||||||
- A copy of the MIT license
|
|
||||||
`@license https://opensource.org/licenses/MIT [MIT LICENSE]`
|
|
||||||
- May include a link for more information
|
|
||||||
`@link http://link.com`
|
|
||||||
|
|
||||||
### Functions
|
|
||||||
Functions must be prefaced with a doc-block following this style:
|
|
||||||
```
|
|
||||||
/**
|
|
||||||
* Intended as a self-destruct session. If the specified session does not
|
|
||||||
* exist, it is created. If the specified session does exist, it will be
|
|
||||||
* destroyed and returned.
|
|
||||||
*
|
|
||||||
* @param string $name - Session name to be created or checked
|
|
||||||
* @param string $string - The string to be used if session needs to be
|
|
||||||
* created. (optional)
|
|
||||||
*
|
|
||||||
* @return bool|string - Returns bool if creating, and a string if the
|
|
||||||
* check is successful.
|
|
||||||
*/
|
|
||||||
```
|
|
||||||
|
|
||||||
From top to bottom:
|
|
||||||
- There must be a description of the functions intended usage on the second line
|
|
||||||
- All parameters should be documented like this
|
|
||||||
`@param [type] $name - description`
|
|
||||||
- Any function with a return statement must also be documented as such
|
|
||||||
`@return [type] - description`
|
|
||||||
|
|
||||||
## Creating a Pull Request
|
|
||||||
This is a simple explanation of how to create a pull request for changes to TheTempusProject. You can find a detailed walk-through on how to [create a pull request](https://help.github.com/articles/creating-a-pull-request/) on github.
|
|
||||||
|
|
||||||
1. First ensure you have followed all the contributing guidelines
|
|
||||||
2. Squash your merge into a single revision. This will make it easier to view the changes as a whole.
|
|
||||||
3. You can submit a pull request [here](https://github.com/TheTempusProject/TheTempusProject/compare)
|
|
||||||
4. Please submit all pull requests to the dev branch or they will be ignored.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
add spaces after everything
|
|
||||||
|
|
||||||
avoid "return;" just make the previous line the return
|
|
||||||
|
|
||||||
use []
|
|
||||||
|
|
||||||
do not use array()
|
|
||||||
|
|
||||||
do not use (array)
|
|
||||||
|
|
||||||
do not add useless variables
|
|
||||||
|
|
||||||
if you are going to set something or check if its empty, just never set it to begin with, don't set it to null
|
|
25
LICENSE
25
LICENSE
@ -1,25 +0,0 @@
|
|||||||
Copyright (c) 2024-present Joey Kimsey
|
|
||||||
|
|
||||||
Portions of this software are licensed as follows:
|
|
||||||
|
|
||||||
* All content residing under the "app/" directory of this repository, excluding "app/plugins/"; is licensed under "Creative Commons: CC BY-SA 4.0 license".
|
|
||||||
* All third party components incorporated into The Tempus Project Software including plugins are licensed under the original license provided by the owner of the applicable component.
|
|
||||||
* Content outside of the above mentioned directories or restrictions above is available under the "MIT Expat" license as defined below.
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
131
README.md
131
README.md
@ -1,131 +0,0 @@
|
|||||||
# The Tempus Project
|
|
||||||
|
|
||||||
_Rapid Prototyping Framework built on PHP utilizing the MVC pattern with a Bootstrap front-end_
|
|
||||||
|
|
||||||
__Developer(s):__
|
|
||||||
|
|
||||||
- __Joey Kimsey__ - _Lead Developer_
|
|
||||||
|
|
||||||
The aim of this project is to provide a simple and stable platform from which to easily add functionality. The goal being the ability to quickly build and test new projects with a lightweight ecosystem to help.
|
|
||||||
|
|
||||||
**Notice: This code is in _still_ not production ready. This framework is provided as is, use at your own risk.**\
|
|
||||||
I am working very hard to ensure the system is safe and reliable enough for me to endorse its widespread use. Unfortunately, it still needs a lot of QA and improvements.
|
|
||||||
|
|
||||||
## Table of contents
|
|
||||||
|
|
||||||
[[_TOC_]]
|
|
||||||
|
|
||||||
## Find Us
|
|
||||||
|
|
||||||
* [DockerHub](https://hub.docker.com/repositories/thetempusproject)
|
|
||||||
* [Packagist](https://packagist.org/packages/thetempusproject/)
|
|
||||||
* [GitLab](https://git.thetempusproject.com/the-tempus-project/thetempusproject)
|
|
||||||
|
|
||||||
## Summary
|
|
||||||
|
|
||||||
The Tempus Project is a PHP application utilizing the MVC pattern to serve up simple pages and APIs with minimal effort. It requires a MySQL database to function and is designed to run equally well with nginx or apache powering the webserver. Most of the core functionality is developed in house and provided through dependencies. At this time, the frontend is driven on bootstrap 3 and FontAwesome for simplicity.
|
|
||||||
|
|
||||||
## Features
|
|
||||||
|
|
||||||
- A Plugin system that allows plug-and-play functionality
|
|
||||||
- A User management system
|
|
||||||
- groups
|
|
||||||
- permissions
|
|
||||||
- preferences
|
|
||||||
- registration and recovery
|
|
||||||
(All Controlled dynamically via our plugin interface)
|
|
||||||
- Compatibility with both Apache and NGINX
|
|
||||||
- Built with Bootstrap with a focus on mobile compatibility
|
|
||||||
- Incredibly easy to set-up, deploy, and develop
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
The preferred method for installation is [Composer](#composer) but special attention has been given to installation and usage [without Composer](#composer).
|
|
||||||
|
|
||||||
### Composer
|
|
||||||
|
|
||||||
The simplest method to start a new project is to use composer to create a new project and automatically clone all the necessary files:
|
|
||||||
|
|
||||||
#### via create-project
|
|
||||||
|
|
||||||
```
|
|
||||||
composer create-project thetempusproject/thetempusproject test-app
|
|
||||||
```
|
|
||||||
|
|
||||||
#### via clone & install
|
|
||||||
|
|
||||||
1. Clone the directory to wherever you want to install the framework.
|
|
||||||
`git clone https://git.thetempusproject.com/the-tempus-project/thetempusproject.git <test-app>`
|
|
||||||
1. Open your terminal to the directory you previously cloned the repository.
|
|
||||||
`cd <test-app>`
|
|
||||||
1. Install using composer:
|
|
||||||
`php composer.phar install`
|
|
||||||
|
|
||||||
### Manually
|
|
||||||
|
|
||||||
1. Clone the directory to wherever you want to install the framework.
|
|
||||||
`git clone https://git.thetempusproject.com/the-tempus-project/thetempusproject.git <test-app>`
|
|
||||||
1. Open your terminal to the directory you previously cloned the repository.
|
|
||||||
`cd <test-app>/`
|
|
||||||
1. Clone the dependency directories to the vendor/ folder.
|
|
||||||
```
|
|
||||||
cd vendor/
|
|
||||||
git clone https://git.thetempusproject.com/the-tempus-project/bedrock.git bedrock
|
|
||||||
git clone https://git.thetempusproject.com/the-tempus-project/canary.git canary
|
|
||||||
git clone https://git.thetempusproject.com/the-tempus-project/hermes.git hermes
|
|
||||||
git clone https://git.thetempusproject.com/the-tempus-project/houdini.git houdini
|
|
||||||
```
|
|
||||||
|
|
||||||
__Note:__ The autoloader should automatically detect and use the dependencies, but they need to be sorted into the folders ans shown above.
|
|
||||||
|
|
||||||
|
|
||||||
## Docker
|
|
||||||
|
|
||||||
To enable quick deployment and collaboration The Tempus Project is distributed with the files to build your own docker images or stack with apache or nginx The included `docker-compose.yml` will load up an entire stack including apache and nginx, as well as a MySQL server with phpmyadmin.
|
|
||||||
|
|
||||||
You will need docker installed on your system then you can either download the latest images from DockerHud:
|
|
||||||
|
|
||||||
```
|
|
||||||
docker pull thetempusproject/ttp-apache
|
|
||||||
docker pull thetempusproject/ttp-nginx
|
|
||||||
```
|
|
||||||
|
|
||||||
Or you can build your own images from this repository. More information can be found in the included README files:
|
|
||||||
|
|
||||||
* [Apache Image](docker/ttp-apache/README.md)
|
|
||||||
* [Nginx Image](docker/ttp-nginx/README.md)
|
|
||||||
|
|
||||||
### Docker-Compose
|
|
||||||
|
|
||||||
The Docker stack included here will build new versions of the nginx and apache webserver and launch them in individual containers. It will also create 2 more containers; one for php, and one for phpmyadmin.
|
|
||||||
|
|
||||||
```
|
|
||||||
docker-compose -f docker-compose.yml up --build -d --no-cache
|
|
||||||
```
|
|
||||||
|
|
||||||
__Note:__ If you cloned the repository from git, you will need to copy the `docker/.env.example` to `.env` in the root directory and update the contents before proceeding with docker-compose.
|
|
||||||
|
|
||||||
## Contributing
|
|
||||||
|
|
||||||
TheTempusProject is an open source project and welcomes community contributions. Please refer to the [Contributing file](CONTRIBUTING.md) for more details.
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
See the [LICENSE](LICENSE) file for licensing information as it pertains to files in this repository.
|
|
||||||
|
|
||||||
## Known Issues
|
|
||||||
|
|
||||||
- [ ] The blog plugin should add a welcome post during the installResources step of the installer. It doesn't work right now.
|
|
||||||
|
|
||||||
## Currently being developed
|
|
||||||
|
|
||||||
- [ ] Adding documentation
|
|
||||||
- [ ] Unit testing
|
|
||||||
|
|
||||||
## Future updates
|
|
||||||
|
|
||||||
- [ ] Expansion of PDO to allow different database types
|
|
||||||
- [ ] Update installer to account for database deltas, allowing easy updating.
|
|
||||||
- [ ] Implement uniformity in terms of error reporting, exceptions, logging.
|
|
||||||
- [ ] I want to make an api that allows you to download and install new plugins from a centralized repository
|
|
||||||
- [ ] i want plugin instalation to be compatible with composer for easier management of added plugins.
|
|
@ -38,8 +38,8 @@ if ( ! defined( 'CONFIG_DIRECTORY' ) ) {
|
|||||||
# Tempus Debugger
|
# Tempus Debugger
|
||||||
define( 'CANARY_SECURE_HASH', 'd73ed7591a30f0ca7d686a0e780f0d05' );
|
define( 'CANARY_SECURE_HASH', 'd73ed7591a30f0ca7d686a0e780f0d05' );
|
||||||
# Tempus Project Core
|
# Tempus Project Core
|
||||||
define( 'APP_NAME', 'The Tempus Project');
|
define( 'APP_NAME', 'Joey Kimsey');
|
||||||
define( 'TP_DEFAULT_LOGO', 'images/logoWhite.png');
|
define( 'TP_DEFAULT_LOGO', 'images/logo.png');
|
||||||
// Check
|
// Check
|
||||||
define( 'MINIMUM_PHP_VERSION', 8.1);
|
define( 'MINIMUM_PHP_VERSION', 8.1);
|
||||||
// Cookies
|
// Cookies
|
||||||
|
64
app/controllers/downloads.php
Normal file
64
app/controllers/downloads.php
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* app/controllers/hermes.php
|
||||||
|
*
|
||||||
|
* This is the hermes controller.
|
||||||
|
*
|
||||||
|
* @version 3.0
|
||||||
|
* @author Joey Kimsey <Joey@thetempusproject.com>
|
||||||
|
* @link https://TheTempusProject.com
|
||||||
|
* @license https://opensource.org/licenses/MIT [MIT LICENSE]
|
||||||
|
*/
|
||||||
|
namespace TheTempusProject\Controllers;
|
||||||
|
|
||||||
|
use TheTempusProject\Hermes\Functions\Redirect;
|
||||||
|
use TheTempusProject\Bedrock\Functions\Session;
|
||||||
|
use TheTempusProject\Bedrock\Functions\Check;
|
||||||
|
use TheTempusProject\Bedrock\Functions\Input;
|
||||||
|
use TheTempusProject\Hermes\Functions\Route as Routes;
|
||||||
|
use TheTempusProject\Houdini\Classes\Issues;
|
||||||
|
use TheTempusProject\Houdini\Classes\Views;
|
||||||
|
use TheTempusProject\Houdini\Classes\Components;
|
||||||
|
use TheTempusProject\Houdini\Classes\Template;
|
||||||
|
use TheTempusProject\Classes\Controller;
|
||||||
|
use TheTempusProject\Classes\Forms;
|
||||||
|
use TheTempusProject\TheTempusProject as App;
|
||||||
|
|
||||||
|
class Downloads extends Controller {
|
||||||
|
public function index() {
|
||||||
|
Session::flash( 'success', 'Unknown download.' );
|
||||||
|
return Redirect::to( 'home/index' );
|
||||||
|
}
|
||||||
|
|
||||||
|
public function resume( $extension = 'none' ) {
|
||||||
|
if ( ! in_array( $extension, ['docx','pdf','md','txt'] ) ) {
|
||||||
|
Session::flash( 'success', 'Unknown download.' );
|
||||||
|
return Redirect::to( 'home/index' );
|
||||||
|
}
|
||||||
|
|
||||||
|
$file = APP_ROOT_DIRECTORY . 'downloads' . DIRECTORY_SEPARATOR . 'JoeyKimsey-resume-2025.' . $extension;
|
||||||
|
|
||||||
|
// Check if the file exists
|
||||||
|
if (file_exists($file)) {
|
||||||
|
// Set headers to force the download
|
||||||
|
header('Content-Description: File Transfer');
|
||||||
|
header('Content-Type: application/octet-stream');
|
||||||
|
header('Content-Disposition: attachment; filename="' . basename($file) . '"');
|
||||||
|
header('Expires: 0');
|
||||||
|
header('Cache-Control: must-revalidate');
|
||||||
|
header('Pragma: public');
|
||||||
|
header('Content-Length: ' . filesize($file));
|
||||||
|
|
||||||
|
// Clear the output buffer
|
||||||
|
ob_clean();
|
||||||
|
flush();
|
||||||
|
|
||||||
|
// Read and output the file
|
||||||
|
readfile($file);
|
||||||
|
exit;
|
||||||
|
} else {
|
||||||
|
Session::flash( 'success', 'Unknown download.' );
|
||||||
|
return Redirect::to( 'home/index' );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -27,8 +27,24 @@ use TheTempusProject\TheTempusProject as App;
|
|||||||
class Home extends Controller {
|
class Home extends Controller {
|
||||||
public function index() {
|
public function index() {
|
||||||
self::$title = '{SITENAME}';
|
self::$title = '{SITENAME}';
|
||||||
self::$pageDescription = '{SITENAME} is here to provide you a better, faster, and easier - way to create and manage your own web applications.';
|
self::$pageDescription = 'Joey Kimsey is a web app developer with nearly a decade of professional experience and another decade of hands on experience.';
|
||||||
Views::view( 'index' );
|
$optionValues = [
|
||||||
|
(object) [ "post" => "python-dev", "option" => "Python Developer" ],
|
||||||
|
(object) [ "post" => "ecommerce-coder", "option" => "eCommerce Coder" ],
|
||||||
|
(object) [ "post" => "php-experience", "option" => "PHP Developer" ],
|
||||||
|
(object) [ "post" => "ai-experience", "option" => "GPT-Agent" ],
|
||||||
|
(object) [ "post" => "operations-experience", "option" => "DevOps Engineer" ],
|
||||||
|
(object) [ "post" => "lua-dev", "option" => "Lua Developer" ],
|
||||||
|
(object) [ "post" => "version-control", "option" => "Git Goblin" ],
|
||||||
|
(object) [ "post" => "laravel-experience", "option" => "Laravel Developer" ],
|
||||||
|
(object) [ "post" => "database-experience", "option" => "Database Admin" ],
|
||||||
|
(object) [ "post" => "education", "option" => "Student" ],
|
||||||
|
(object) [ "post" => "full-stack-developer", "option" => "Full-Stack Developer" ],
|
||||||
|
(object) [ "post" => "stripe-certified-developer", "option" => "Stripe Certified Developer" ],
|
||||||
|
|
||||||
|
];
|
||||||
|
shuffle($optionValues);
|
||||||
|
Views::view( 'index', $optionValues );
|
||||||
}
|
}
|
||||||
|
|
||||||
public function login() {
|
public function login() {
|
||||||
@ -91,19 +107,13 @@ class Home extends Controller {
|
|||||||
|
|
||||||
public function about() {
|
public function about() {
|
||||||
self::$title = 'About - {SITENAME}';
|
self::$title = 'About - {SITENAME}';
|
||||||
self::$pageDescription = '{SITENAME} was started by a developer with years of industry experience which has lead to a refined no-nonsense tool for everyone. Find out more about us here.';
|
self::$pageDescription = 'Just a bit more info on me.';
|
||||||
Views::view( 'about' );
|
Views::view( 'about' );
|
||||||
}
|
}
|
||||||
|
|
||||||
public function privacy() {
|
public function hire() {
|
||||||
self::$title = 'Privacy Policy - {SITENAME}';
|
self::$title = 'Consulting and Freelance - {SITENAME}';
|
||||||
self::$pageDescription = 'At {SITENAME} you privacy is very important to us. On this page you can find a detailed outline of all the information we collect and how its used.';
|
self::$pageDescription = 'More details on how to hire me for consulting or freelance work.';
|
||||||
Views::view( 'privacy' );
|
Views::view( 'hire' );
|
||||||
}
|
|
||||||
|
|
||||||
public function faq() {
|
|
||||||
self::$title = 'Frequently Asked Questions - {SITENAME}';
|
|
||||||
self::$pageDescription = 'Many times, we aren\'t the first to ask why or how something works. Here you will find a list of {SITENAME} commonly asked questions and our best answers.' ;
|
|
||||||
Views::view( 'faq' );
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -274,4 +274,21 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
var popoverList = popoverTriggerList.map(function (popoverTriggerEl) {
|
var popoverList = popoverTriggerList.map(function (popoverTriggerEl) {
|
||||||
return new bootstrap.Popover(popoverTriggerEl);
|
return new bootstrap.Popover(popoverTriggerEl);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
document.addEventListener('DOMContentLoaded', function () {
|
||||||
|
const postSelector = document.getElementById('postSelector');
|
||||||
|
const goToPostButton = document.getElementById('goToPost');
|
||||||
|
|
||||||
|
if ( postSelector && goToPostButton ) {
|
||||||
|
goToPostButton.addEventListener('click', function () {
|
||||||
|
const selectedValue = postSelector.value;
|
||||||
|
if (selectedValue) {
|
||||||
|
const url = `/blog/post/${selectedValue}`;
|
||||||
|
window.location.href = url;
|
||||||
|
} else {
|
||||||
|
alert('Please select an option before proceeding.');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
@ -46,12 +46,12 @@
|
|||||||
|
|
||||||
<!-- Centered Logo (Now inside normal document flow) -->
|
<!-- Centered Logo (Now inside normal document flow) -->
|
||||||
<a href="/" class="align-items-center text-white text-decoration-none d-flex d-md-none">
|
<a href="/" class="align-items-center text-white text-decoration-none d-flex d-md-none">
|
||||||
<img src="{ROOT_URL}{LOGO}" width="40" height="32" alt="{SITENAME} Logo" class="bi">
|
<img src="{ROOT_URL}images/logo.png" width="40" height="32" alt="{SITENAME} Logo" class="bi">
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<!-- Logo (Normal Position for Large Screens) -->
|
<!-- Logo (Normal Position for Large Screens) -->
|
||||||
<a href="/" class="align-items-center text-white text-decoration-none d-none d-md-flex">
|
<a href="/" class="align-items-center text-white text-decoration-none d-none d-md-flex">
|
||||||
<img src="{ROOT_URL}{LOGO}" width="40" height="32" alt="{SITENAME} Logo" class="bi">
|
<img src="{ROOT_URL}{LOGO}" height="48" alt="{SITENAME} Logo" class="bi">
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<div class="navbar-expand-md flex-grow-1">
|
<div class="navbar-expand-md flex-grow-1">
|
||||||
|
@ -1,63 +0,0 @@
|
|||||||
<?php
|
|
||||||
/**
|
|
||||||
* app/plugins/bugreport/controllers/admin/bugreport.php
|
|
||||||
*
|
|
||||||
* This is the bug report admin controller.
|
|
||||||
*
|
|
||||||
* @package TP BugReports
|
|
||||||
* @version 5.0.1
|
|
||||||
* @author Joey Kimsey <Joey@thetempusproject.com>
|
|
||||||
* @link https://TheTempusProject.com
|
|
||||||
* @license https://opensource.org/licenses/MIT [MIT LICENSE]
|
|
||||||
*/
|
|
||||||
namespace TheTempusProject\Controllers\Admin;
|
|
||||||
|
|
||||||
use TheTempusProject\Classes\AdminController;
|
|
||||||
use TheTempusProject\Bedrock\Functions\Input;
|
|
||||||
use TheTempusProject\Houdini\Classes\Issues;
|
|
||||||
use TheTempusProject\Houdini\Classes\Views;
|
|
||||||
use TheTempusProject\Houdini\Classes\Navigation;
|
|
||||||
use TheTempusProject\Houdini\Classes\Components;
|
|
||||||
use TheTempusProject\Models\Bugreport as BugreportModel;
|
|
||||||
|
|
||||||
class Bugreport extends AdminController {
|
|
||||||
protected static $bugreport;
|
|
||||||
|
|
||||||
public function __construct() {
|
|
||||||
parent::__construct();
|
|
||||||
self::$bugreport = new BugreportModel;
|
|
||||||
self::$title = 'Admin - Bug Reports';
|
|
||||||
$view = Navigation::activePageSelect( 'nav.admin', '/admin/bugreport' );
|
|
||||||
Components::set( 'ADMINNAV', $view );
|
|
||||||
}
|
|
||||||
|
|
||||||
public function index( $data = null ) {
|
|
||||||
Views::view( 'bugreport.admin.list', self::$bugreport->listPaginated() );
|
|
||||||
}
|
|
||||||
|
|
||||||
public function view( $id = null ) {
|
|
||||||
$data = self::$bugreport->findById( $id );
|
|
||||||
if ( $data !== false ) {
|
|
||||||
return Views::view( 'bugreport.admin.view', $data );
|
|
||||||
}
|
|
||||||
Issues::add( 'error', 'Report not found.' );
|
|
||||||
$this->index();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function delete( $data = null ) {
|
|
||||||
if ( Input::exists( 'submit' ) ) {
|
|
||||||
$data = Input::post( 'BR_' );
|
|
||||||
}
|
|
||||||
if ( self::$bugreport->delete( (array) $data ) ) {
|
|
||||||
Issues::add( 'success', 'Bug Report Deleted' );
|
|
||||||
} else {
|
|
||||||
Issues::add( 'error', 'There was an error with your request.' );
|
|
||||||
}
|
|
||||||
$this->index();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function clear( $data = null ) {
|
|
||||||
self::$bugreport->empty();
|
|
||||||
$this->index();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,52 +0,0 @@
|
|||||||
<?php
|
|
||||||
/**
|
|
||||||
* app/plugins/bugreport/controllers/bugreport.php
|
|
||||||
*
|
|
||||||
* This is the bug reports controller.
|
|
||||||
*
|
|
||||||
* @package TP BugReports
|
|
||||||
* @version 5.0.1
|
|
||||||
* @author Joey Kimsey <Joey@thetempusproject.com>
|
|
||||||
* @link https://TheTempusProject.com
|
|
||||||
* @license https://opensource.org/licenses/MIT [MIT LICENSE]
|
|
||||||
*/
|
|
||||||
namespace TheTempusProject\Controllers;
|
|
||||||
|
|
||||||
use TheTempusProject\Hermes\Functions\Redirect;
|
|
||||||
use TheTempusProject\Bedrock\Functions\Check;
|
|
||||||
use TheTempusProject\Bedrock\Functions\Input;
|
|
||||||
use TheTempusProject\Bedrock\Functions\Session;
|
|
||||||
use TheTempusProject\Houdini\Classes\Issues;
|
|
||||||
use TheTempusProject\Houdini\Classes\Views;
|
|
||||||
use TheTempusProject\Classes\Controller;
|
|
||||||
use TheTempusProject\Classes\Forms;
|
|
||||||
use TheTempusProject\Models\Bugreport as BugreportModel;
|
|
||||||
use TheTempusProject\TheTempusProject as App;
|
|
||||||
|
|
||||||
class Bugreport extends Controller {
|
|
||||||
protected static $bugreport;
|
|
||||||
|
|
||||||
public function index() {
|
|
||||||
self::$bugreport = new BugreportModel;
|
|
||||||
self::$title = 'Bug Report - {SITENAME}';
|
|
||||||
self::$pageDescription = 'On this page you can submit a bug report for the site.';
|
|
||||||
if ( !App::$isLoggedIn ) {
|
|
||||||
return Issues::add( 'notice', 'You must be logged in to report bugs.' );
|
|
||||||
}
|
|
||||||
if ( !Input::exists() ) {
|
|
||||||
return Views::view( 'bugreport.create' );
|
|
||||||
}
|
|
||||||
if ( !Forms::check( 'bugreport' ) ) {
|
|
||||||
Issues::add( 'error', [ 'There was an error with your report.' => Check::userErrors() ] );
|
|
||||||
return Views::view( 'bugreport.create' );
|
|
||||||
}
|
|
||||||
$result = self::$bugreport->create( App::$activeUser->ID, Input::post( 'url' ), Input::post( 'ourl' ), Input::post( 'repeat' ), Input::post( 'entry' ) );
|
|
||||||
if ( false != $result ) {
|
|
||||||
Session::flash( 'success', 'Your Bug Report has been received. We may contact you for more information at the email address you provided.' );
|
|
||||||
Redirect::to( 'home/index' );
|
|
||||||
} else {
|
|
||||||
Issues::add( 'error', 'There was an unresolved error while submitting your report.' );
|
|
||||||
return Views::view( 'bugreport.create' );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,51 +0,0 @@
|
|||||||
<?php
|
|
||||||
/**
|
|
||||||
* app/plugins/bugreport/forms.php
|
|
||||||
*
|
|
||||||
* This houses all of the form checking functions for this plugin.
|
|
||||||
*
|
|
||||||
* @package TP BugReports
|
|
||||||
* @version 5.0.1
|
|
||||||
* @author Joey Kimsey <Joey@thetempusproject.com>
|
|
||||||
* @link https://TheTempusProject.com
|
|
||||||
* @license https://opensource.org/licenses/MIT [MIT LICENSE]
|
|
||||||
*/
|
|
||||||
namespace TheTempusProject\Plugins\Bugreport;
|
|
||||||
|
|
||||||
use TheTempusProject\Bedrock\Functions\Input;
|
|
||||||
use TheTempusProject\Classes\Forms;
|
|
||||||
|
|
||||||
class BugReportForms extends Forms {
|
|
||||||
/**
|
|
||||||
* Adds these functions to the form list.
|
|
||||||
*/
|
|
||||||
public function __construct() {
|
|
||||||
self::addHandler( 'bugreport', __CLASS__, 'bugreport' );
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Validates the bug report form.
|
|
||||||
*
|
|
||||||
* @return {bool}
|
|
||||||
*/
|
|
||||||
public static function bugreport() {
|
|
||||||
if ( !self::url( Input::post( 'url' ) ) ) {
|
|
||||||
self::addUserError( 'Invalid url.' );
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if ( !self::url( Input::post( 'ourl' ) ) ) {
|
|
||||||
self::addUserError( 'Invalid original url.' );
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if ( !self::tf( Input::post( 'repeat' ) ) ) {
|
|
||||||
self::addUserError( 'Invalid repeat value.' );
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if ( !self::token() ) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
new BugReportForms;
|
|
@ -1,100 +0,0 @@
|
|||||||
<?php
|
|
||||||
/**
|
|
||||||
* app/plugins/bugreport/models/bugreport.php
|
|
||||||
*
|
|
||||||
* This class is used for the manipulation of the bugreports database table.
|
|
||||||
*
|
|
||||||
* @package TP BugReports
|
|
||||||
* @version 5.0.1
|
|
||||||
* @author Joey Kimsey <Joey@thetempusproject.com>
|
|
||||||
* @link https://TheTempusProject.com
|
|
||||||
* @license https://opensource.org/licenses/MIT [MIT LICENSE]
|
|
||||||
*/
|
|
||||||
namespace TheTempusProject\Models;
|
|
||||||
|
|
||||||
use TheTempusProject\Bedrock\Functions\Check;
|
|
||||||
use TheTempusProject\Canary\Bin\Canary as Debug;
|
|
||||||
use TheTempusProject\Canary\Classes\CustomException;
|
|
||||||
use TheTempusProject\Classes\DatabaseModel;
|
|
||||||
use TheTempusProject\Plugins\Bugreport as Plugin;
|
|
||||||
use TheTempusProject\TheTempusProject as App;
|
|
||||||
|
|
||||||
class Bugreport extends DatabaseModel {
|
|
||||||
public $tableName = 'bugreports';
|
|
||||||
public $databaseMatrix = [
|
|
||||||
[ 'userID', 'int', '11' ],
|
|
||||||
[ 'time', 'int', '10' ],
|
|
||||||
[ 'repeat', 'varchar', '5' ],
|
|
||||||
[ 'ourl', 'varchar', '256' ],
|
|
||||||
[ 'url', 'varchar', '256' ],
|
|
||||||
[ 'ip', 'varchar', '15' ],
|
|
||||||
[ 'description', 'text', '' ],
|
|
||||||
];
|
|
||||||
public $plugin;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The model constructor.
|
|
||||||
*/
|
|
||||||
public function __construct() {
|
|
||||||
parent::__construct();
|
|
||||||
$this->plugin = new Plugin;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This function parses the bug reports description and
|
|
||||||
* separates it into separate keys in the array.
|
|
||||||
*
|
|
||||||
* @param array $data - The data being parsed.
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
public function filter( $data, $params = [] ) {
|
|
||||||
foreach ( $data as $instance ) {
|
|
||||||
if ( !is_object( $instance ) ) {
|
|
||||||
$instance = $data;
|
|
||||||
$end = true;
|
|
||||||
}
|
|
||||||
$instance->submittedBy = self::$user->getUsername( $instance->userID );
|
|
||||||
$instance->repeatText = ( $instance->repeat ? 'yes' : 'no' );
|
|
||||||
$out[] = $instance;
|
|
||||||
if ( !empty( $end ) ) {
|
|
||||||
$out = $out[0];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return $out;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Logs a Bug Report form.
|
|
||||||
*
|
|
||||||
* @param int $ID the user ID submitting the form
|
|
||||||
* @param string $url the url
|
|
||||||
* @param string $o_url the original url
|
|
||||||
* @param int $repeat is repeatable?
|
|
||||||
* @param string $description_ description of the event.
|
|
||||||
*
|
|
||||||
* @return null
|
|
||||||
*/
|
|
||||||
public function create( $ID, $url, $oUrl, $repeat, $description ) {
|
|
||||||
if ( !$this->plugin->checkEnabled() ) {
|
|
||||||
Debug::info( 'Bug Reporting is disabled in the config.' );
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
$fields = [
|
|
||||||
'userID' => App::$activeUser->ID,
|
|
||||||
'time' => time(),
|
|
||||||
'repeat' => $repeat,
|
|
||||||
'ourl' => $oUrl,
|
|
||||||
'url' => $url,
|
|
||||||
'ip' => $_SERVER['REMOTE_ADDR'],
|
|
||||||
'description' => $description,
|
|
||||||
];
|
|
||||||
if ( !self::$db->insert( $this->tableName, $fields ) ) {
|
|
||||||
new CustomException( 'bugreportsCreate' );
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return self::$db->lastId();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,65 +0,0 @@
|
|||||||
<?php
|
|
||||||
/**
|
|
||||||
* app/plugins/bugreport/plugin.php
|
|
||||||
*
|
|
||||||
* This houses all of the main plugin info and functionality.
|
|
||||||
*
|
|
||||||
* @package TP BugReports
|
|
||||||
* @version 5.0.1
|
|
||||||
* @author Joey Kimsey <Joey@thetempusproject.com>
|
|
||||||
* @link https://TheTempusProject.com
|
|
||||||
* @license https://opensource.org/licenses/MIT [MIT LICENSE]
|
|
||||||
*/
|
|
||||||
namespace TheTempusProject\Plugins;
|
|
||||||
|
|
||||||
use ReflectionClass;
|
|
||||||
use TheTempusProject\Classes\Installer;
|
|
||||||
use TheTempusProject\Houdini\Classes\Navigation;
|
|
||||||
use TheTempusProject\Classes\Plugin;
|
|
||||||
use TheTempusProject\TheTempusProject as App;
|
|
||||||
|
|
||||||
class Bugreport extends Plugin {
|
|
||||||
public $pluginName = 'TP BugReports';
|
|
||||||
public $pluginAuthor = 'JoeyK';
|
|
||||||
public $pluginWebsite = 'https://TheTempusProject.com';
|
|
||||||
public $modelVersion = '1.0';
|
|
||||||
public $pluginVersion = '3.0';
|
|
||||||
public $pluginDescription = '';
|
|
||||||
public $configName = 'bugreports';
|
|
||||||
public $configMatrix = [
|
|
||||||
'enabled' => [
|
|
||||||
'type' => 'radio',
|
|
||||||
'pretty' => 'Enable Bug reporting.',
|
|
||||||
'default' => true,
|
|
||||||
],
|
|
||||||
'sendEmail' => [
|
|
||||||
'type' => 'radio',
|
|
||||||
'pretty' => 'Email the user after submitting.',
|
|
||||||
'default' => true,
|
|
||||||
],
|
|
||||||
'emailTemplate' => [
|
|
||||||
'type' => 'text',
|
|
||||||
'pretty' => 'Email Template',
|
|
||||||
'default' => 'BugReportEmail',
|
|
||||||
],
|
|
||||||
];
|
|
||||||
public $permissionMatrix = [
|
|
||||||
'canSendBugReports' => [
|
|
||||||
'pretty' => 'Can Submit Bug Reports',
|
|
||||||
'default' => false,
|
|
||||||
],
|
|
||||||
];
|
|
||||||
public $contact_footer_links = [
|
|
||||||
[
|
|
||||||
'text' => 'Report a Bug',
|
|
||||||
'url' => '{ROOT_URL}bugreport',
|
|
||||||
'filter' => 'loggedin',
|
|
||||||
],
|
|
||||||
];
|
|
||||||
public $admin_links = [
|
|
||||||
[
|
|
||||||
'text' => '<i class="fa fa-fw fa-bug"></i> Bug Reports',
|
|
||||||
'url' => '{ROOT_URL}admin/bugreport',
|
|
||||||
],
|
|
||||||
];
|
|
||||||
}
|
|
@ -1,45 +0,0 @@
|
|||||||
<div class="context-main-bg context-main p-3">
|
|
||||||
<legend class="text-center">Bug Reports</legend>
|
|
||||||
<hr>
|
|
||||||
{ADMIN_BREADCRUMBS}
|
|
||||||
<form action="{ROOT_URL}admin/bugreport/delete" method="post">
|
|
||||||
<table class="table table-striped">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th style="width: 5%">ID</th>
|
|
||||||
<th style="width: 20%">Time</th>
|
|
||||||
<th style="width: 60%">Description</th>
|
|
||||||
<th style="width: 5%"></th>
|
|
||||||
<th style="width: 5%"></th>
|
|
||||||
<th style="width: 5%">
|
|
||||||
<input type="checkbox" onchange="checkAll(this)" name="check.br" value="BR_[]">
|
|
||||||
</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{LOOP}
|
|
||||||
<tr>
|
|
||||||
<td align="center">{ID}</td>
|
|
||||||
<td align="center">{DTC}{time}{/DTC}</td>
|
|
||||||
<td>{description}</td>
|
|
||||||
<td><a href="{ROOT_URL}admin/bugreport/view/{ID}" class="btn btn-sm btn-primary"><i class="fa fa-fw fa-upload"></i></a></td>
|
|
||||||
<td><a href="{ROOT_URL}admin/bugreport/delete/{ID}" class="btn btn-sm btn-danger"><i class="fa fa-fw fa-trash"></i></a></td>
|
|
||||||
<td>
|
|
||||||
<input type="checkbox" value="{ID}" name="BR_[]">
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
{/LOOP}
|
|
||||||
{ALT}
|
|
||||||
<tr>
|
|
||||||
<td align="center" colspan="6">
|
|
||||||
No results to show.
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
{/ALT}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
<button name="submit" value="submit" type="submit" class="btn btn-sm btn-danger"><i class="fa fa-fw fa-trash"></i></button>
|
|
||||||
</form>
|
|
||||||
<br>
|
|
||||||
<a href="{ROOT_URL}admin/bugreport/clear">clear all</a>
|
|
||||||
</div>
|
|
@ -1,69 +0,0 @@
|
|||||||
<div class="container py-4">
|
|
||||||
<div class="row justify-content-center">
|
|
||||||
<div class="col-md-8">
|
|
||||||
{ADMIN_BREADCRUMBS}
|
|
||||||
<div class="card shadow">
|
|
||||||
<!-- Card Header -->
|
|
||||||
<div class="card-header text-center bg-dark text-white">
|
|
||||||
<h3 class="card-title mb-0">Bug Report</h3>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Card Body -->
|
|
||||||
<div class="card-body">
|
|
||||||
<div class="row align-items-center">
|
|
||||||
<!-- Log Details -->
|
|
||||||
<table class="table table-borderless">
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<th scope="row">ID:</th>
|
|
||||||
<td>{ID}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th scope="row">Time submitted</th>
|
|
||||||
<td>{DTC}{time}{/DTC}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th scope="row">URL:</th>
|
|
||||||
<td><a href="{URL}">{URL}</a></td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th scope="row">Original URL</th>
|
|
||||||
<td><a href="{OURL}">{OURL}</a></td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th scope="row">Multiple occurrences?</th>
|
|
||||||
<td>{repeatText}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th scope="row">IP:</th>
|
|
||||||
<td>{ip}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th scope="row">User:</th>
|
|
||||||
<td><a href="{ROOT_URL}admin/users/view/{userID}">{submittedBy}</a></td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th scope="row" colspan="2">Description:</th>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td colspan="2">{description}</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Admin Controls -->
|
|
||||||
<div class="card-footer text-center">
|
|
||||||
{ADMIN}
|
|
||||||
<form action="{ROOT_URL}admin/bugreport/delete" method="post">
|
|
||||||
<input type="hidden" name="BR_" value="{ID}">
|
|
||||||
<input type="hidden" name="token" value="{TOKEN}">
|
|
||||||
<button name="submit" value="submit" type="submit" class="btn btn-sm btn-danger"><i class="fa fa-fw fa-trash"></i></button>
|
|
||||||
</form>
|
|
||||||
{/ADMIN}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
@ -1,61 +0,0 @@
|
|||||||
<div class="m-2 m-lg-4">
|
|
||||||
<div class="col-12 mx-5 col-sm-10 col-lg-8 mx-auto p-4 rounded shadow-sm context-main-bg">
|
|
||||||
<h2 class="text-center mb-4">Report a Bug</h2>
|
|
||||||
<hr>
|
|
||||||
<p class="text-center text-sm-start">
|
|
||||||
Thank you for visiting our bug reporting page. We value our users' input highly and in an effort to better serve your needs, please fill out the form below to help us address this issue.
|
|
||||||
</p>
|
|
||||||
<p class="text-center text-sm-start">
|
|
||||||
We read each and every bug report submitted, and by submitting this form you allow us to send you a follow-up email.
|
|
||||||
</p>
|
|
||||||
<form method="post">
|
|
||||||
<!-- Page URL -->
|
|
||||||
<div class="mb-3">
|
|
||||||
<label for="url" class="form-label">Page you were trying to reach:</label>
|
|
||||||
<input type="url" name="url" id="url" class="form-control" aria-describedby="urlHelp" required>
|
|
||||||
<small id="urlHelp" class="form-text text-muted">
|
|
||||||
This is the URL of the page you actually received the error.
|
|
||||||
</small>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Referrer URL -->
|
|
||||||
<div class="mb-3">
|
|
||||||
<label for="ourl" class="form-label">Page you were on:</label>
|
|
||||||
<input type="url" name="ourl" id="ourl" class="form-control" aria-describedby="ourlHelp">
|
|
||||||
<small id="ourlHelp" class="form-text text-muted">
|
|
||||||
This is the URL of the page you were on before you received the error.
|
|
||||||
</small>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Repeat Issue -->
|
|
||||||
<div class="mb-3">
|
|
||||||
<label class="form-label">*Has this happened more than once?</label>
|
|
||||||
<div class="form-check">
|
|
||||||
<input class="form-check-input" type="radio" name="repeat" id="repeatNo" value="false" checked>
|
|
||||||
<label class="form-check-label" for="repeatNo">No</label>
|
|
||||||
</div>
|
|
||||||
<div class="form-check">
|
|
||||||
<input class="form-check-input" type="radio" name="repeat" id="repeatYes" value="true">
|
|
||||||
<label class="form-check-label" for="repeatYes">Yes</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Description -->
|
|
||||||
<div class="mb-3">
|
|
||||||
<label for="entry" class="form-label">Describe the error you received: </label>
|
|
||||||
<textarea class="form-control" name="entry" id="entry" rows="6" maxlength="2000" aria-describedby="descHelp" required></textarea>
|
|
||||||
<small id="descHelp" class="form-text text-muted">
|
|
||||||
(max: 2000 characters)
|
|
||||||
</small>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Hidden Token -->
|
|
||||||
<input type="hidden" name="token" value="{TOKEN}">
|
|
||||||
|
|
||||||
<!-- Submit Button -->
|
|
||||||
<div class="text-center">
|
|
||||||
<button type="submit" name="submit" value="submit" class="btn btn-primary btn-lg">Submit</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
@ -1,83 +0,0 @@
|
|||||||
<?php
|
|
||||||
/**
|
|
||||||
* app/plugins/comments/controllers/admin/comments.php
|
|
||||||
*
|
|
||||||
* This is the comments admin controller.
|
|
||||||
*
|
|
||||||
* @package TP Comments
|
|
||||||
* @version 5.0.1
|
|
||||||
* @author Joey Kimsey <Joey@thetempusproject.com>
|
|
||||||
* @link https://TheTempusProject.com
|
|
||||||
* @license https://opensource.org/licenses/MIT [MIT LICENSE]
|
|
||||||
*/
|
|
||||||
namespace TheTempusProject\Controllers\Admin;
|
|
||||||
|
|
||||||
use TheTempusProject\Houdini\Classes\Navigation;
|
|
||||||
use TheTempusProject\Houdini\Classes\Issues;
|
|
||||||
use TheTempusProject\Houdini\Classes\Views;
|
|
||||||
use TheTempusProject\Houdini\Classes\Components;
|
|
||||||
use TheTempusProject\Bedrock\Functions\Input;
|
|
||||||
use TheTempusProject\Bedrock\Functions\Check;
|
|
||||||
use TheTempusProject\Classes\Forms;
|
|
||||||
use TheTempusProject\Classes\AdminController;
|
|
||||||
use TheTempusProject\Models\Comments as CommentsModel;
|
|
||||||
|
|
||||||
class Comments extends AdminController {
|
|
||||||
protected static $comments;
|
|
||||||
|
|
||||||
public function __construct() {
|
|
||||||
parent::__construct();
|
|
||||||
self::$title = 'Admin - Comments';
|
|
||||||
self::$comments = new CommentsModel;
|
|
||||||
$view = Navigation::activePageSelect( 'nav.admin', '/admin/comments' );
|
|
||||||
Components::set( 'ADMINNAV', $view );
|
|
||||||
}
|
|
||||||
|
|
||||||
public function edit( $data = null ) {
|
|
||||||
if ( !Input::exists( 'submit' ) ) {
|
|
||||||
return Views::view( 'comments.admin.edit', self::$comments->findById( $data ) );
|
|
||||||
}
|
|
||||||
if ( !Forms::check( 'editComment' ) ) {
|
|
||||||
Issues::add( 'error', [ 'There was an error with your request.' => Check::userErrors() ] );
|
|
||||||
return Views::view( 'comments.admin.edit', self::$comments->findById( $data ) );
|
|
||||||
}
|
|
||||||
if ( self::$comments->update( $data, Input::post( 'comment' ) ) ) {
|
|
||||||
Issues::add( 'success', 'Comment updated' );
|
|
||||||
} else {
|
|
||||||
return Views::view( 'comments.admin.edit', self::$comments->findById( $data ) );
|
|
||||||
}
|
|
||||||
$this->index();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function viewComments( $contentID = null ) {
|
|
||||||
if ( empty( $contentID ) ) {
|
|
||||||
Issues::add( 'error', 'Content ID not found.' );
|
|
||||||
return $this->index();
|
|
||||||
}
|
|
||||||
$contentData = self::$comments->findById( $data );
|
|
||||||
if ( empty( $contentID ) ) {
|
|
||||||
return Views::view( 'comments.list', $commentData );
|
|
||||||
}
|
|
||||||
Issues::add( 'error', 'Comment not found.' );
|
|
||||||
$this->index();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function delete( $data = null ) {
|
|
||||||
if ( $data == null ) {
|
|
||||||
if ( !Input::exists( 'C_' ) ) {
|
|
||||||
return $this->index();
|
|
||||||
}
|
|
||||||
$data = Input::post( 'C_' );
|
|
||||||
}
|
|
||||||
if ( !self::$comments->delete( $data ) ) {
|
|
||||||
Issues::add( 'error', 'There was an error with your request.' );
|
|
||||||
} else {
|
|
||||||
Issues::add( 'success', 'Comment has been deleted' );
|
|
||||||
}
|
|
||||||
$this->index();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function index() {
|
|
||||||
Views::view( 'comments.admin.list', self::$comments->recent() );
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,36 +0,0 @@
|
|||||||
<?php
|
|
||||||
/**
|
|
||||||
* app/plugins/comments/controllers/moderator.php
|
|
||||||
*
|
|
||||||
* This is the Moderator controller.
|
|
||||||
*
|
|
||||||
* @version 5.0.1
|
|
||||||
* @author Joey Kimsey <Joey@thetempusproject.com>
|
|
||||||
* @link https://TheTempusProject.com
|
|
||||||
* @license https://opensource.org/licenses/MIT [MIT LICENSE]
|
|
||||||
*/
|
|
||||||
namespace TheTempusProject\Controllers;
|
|
||||||
|
|
||||||
use TheTempusProject\Houdini\Classes\Template;
|
|
||||||
use TheTempusProject\Houdini\Classes\Views;
|
|
||||||
use TheTempusProject\Houdini\Classes\Issues;
|
|
||||||
use TheTempusProject\Classes\Controller;
|
|
||||||
use TheTempusProject\TheTempusProject as App;
|
|
||||||
use TheTempusProject\Hermes\Functions\Redirect;
|
|
||||||
use TheTempusProject\Bedrock\Functions\Session;
|
|
||||||
|
|
||||||
class Moderator extends Controller {
|
|
||||||
public function __construct() {
|
|
||||||
parent::__construct();
|
|
||||||
Template::noIndex();
|
|
||||||
if ( !App::$isMod ) {
|
|
||||||
Session::flash( 'error', 'You do not have permission to view this page.' );
|
|
||||||
return Redirect::home();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function index() {
|
|
||||||
self::$title = 'Moderator\'s Area';
|
|
||||||
Views::view( 'comments.moderator' );
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,6 +0,0 @@
|
|||||||
/**
|
|
||||||
* Comments
|
|
||||||
*/
|
|
||||||
.comments {
|
|
||||||
margin-top: 120px;
|
|
||||||
}
|
|
@ -1,69 +0,0 @@
|
|||||||
<?php
|
|
||||||
/**
|
|
||||||
* app/plugins/comments/forms.php
|
|
||||||
*
|
|
||||||
* This houses all of the form checking functions for this plugin.
|
|
||||||
*
|
|
||||||
* @package TP Comments
|
|
||||||
* @version 5.0.1
|
|
||||||
* @author Joey Kimsey <Joey@thetempusproject.com>
|
|
||||||
* @link https://TheTempusProject.com
|
|
||||||
* @license https://opensource.org/licenses/MIT [MIT LICENSE]
|
|
||||||
*/
|
|
||||||
namespace TheTempusProject\Plugins\Comments;
|
|
||||||
|
|
||||||
use TheTempusProject\Bedrock\Functions\Input;
|
|
||||||
use TheTempusProject\Bedrock\Functions\Check;
|
|
||||||
use TheTempusProject\Classes\Forms;
|
|
||||||
|
|
||||||
class CommentsForms extends Forms {
|
|
||||||
/**
|
|
||||||
* Adds these functions to the form list.
|
|
||||||
*/
|
|
||||||
public function __construct() {
|
|
||||||
self::addHandler( 'newComment', __CLASS__, 'newComment' );
|
|
||||||
self::addHandler( 'editComment', __CLASS__, 'editComment' );
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Validates the new comment form.
|
|
||||||
*
|
|
||||||
* @return {bool}
|
|
||||||
*/
|
|
||||||
public static function newComment() {
|
|
||||||
if ( !Input::exists( 'comment' ) ) {
|
|
||||||
Check::addUserError( 'You cannot post a blank comment.' );
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if ( !Input::exists( 'contentId' ) ) {
|
|
||||||
Check::addUserError( 'Content ID was missing.' );
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// these are disabled because i need to figure out a solution for pages where images are wrong
|
|
||||||
// a missing image loads a new token and messes this up
|
|
||||||
// if ( !Check::token() ) {
|
|
||||||
// return false;
|
|
||||||
// }
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Validates the edit comment form.
|
|
||||||
*
|
|
||||||
* @return {bool}
|
|
||||||
*/
|
|
||||||
public static function editComment() {
|
|
||||||
if ( !Input::exists( 'comment' ) ) {
|
|
||||||
Check::addUserError( 'You cannot post a blank comment.' );
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// these are disabled because i need to figure out a solution for pages where images are wrong
|
|
||||||
// a missing image loads a new token and messes this up
|
|
||||||
// if ( !Check::token() ) {
|
|
||||||
// return false;
|
|
||||||
// }
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
new CommentsForms;
|
|
@ -1,184 +0,0 @@
|
|||||||
<?php
|
|
||||||
/**
|
|
||||||
* app/plugins/comments/models/comment.php
|
|
||||||
*
|
|
||||||
* This class is used for the creation, retrieval, and manipulation
|
|
||||||
* of the comments table.
|
|
||||||
*
|
|
||||||
* @package TP Comments
|
|
||||||
* @version 5.0.1
|
|
||||||
* @author Joey Kimsey <Joey@thetempusproject.com>
|
|
||||||
* @link https://TheTempusProject.com
|
|
||||||
* @license https://opensource.org/licenses/MIT [MIT LICENSE]
|
|
||||||
*/
|
|
||||||
namespace TheTempusProject\Models;
|
|
||||||
|
|
||||||
use TheTempusProject\Bedrock\Functions\Check;
|
|
||||||
use TheTempusProject\Canary\Bin\Canary as Debug;
|
|
||||||
use TheTempusProject\Houdini\Classes\Views;
|
|
||||||
use TheTempusProject\Classes\DatabaseModel;
|
|
||||||
use TheTempusProject\TheTempusProject as App;
|
|
||||||
use TheTempusProject\Canary\Classes\CustomException;
|
|
||||||
use TheTempusProject\Houdini\Classes\Filters;
|
|
||||||
|
|
||||||
class Comments extends DatabaseModel {
|
|
||||||
public $tableName = 'comments';
|
|
||||||
public $databaseMatrix = [
|
|
||||||
[ 'author', 'int', '11' ],
|
|
||||||
[ 'contentID', 'int', '11' ],
|
|
||||||
[ 'created', 'int', '10' ],
|
|
||||||
[ 'edited', 'int', '10' ],
|
|
||||||
[ 'approved', 'int', '1' ],
|
|
||||||
[ 'contentType', 'varchar', '32' ],
|
|
||||||
[ 'content', 'text', '' ],
|
|
||||||
];
|
|
||||||
|
|
||||||
public function count( $contentType, $contentID ) {
|
|
||||||
if ( !Check::id( $contentID ) ) {
|
|
||||||
Debug::info( 'Comments: illegal ID.' );
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if ( !Check::dataTitle( $contentType ) ) {
|
|
||||||
Debug::info( 'Comments: illegal Type.' );
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
$where = ['contentType', '=', $contentType, 'AND', 'contentID', '=', $contentID];
|
|
||||||
$data = self::$db->get( $this->tableName, $where );
|
|
||||||
if ( !$data->count() ) {
|
|
||||||
Debug::info( 'No comments found.' );
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return $data->count();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function display( $displayCount, $contentType, $contentID ) {
|
|
||||||
if ( !Check::id( $contentID ) ) {
|
|
||||||
Debug::info( 'Comments: illegal ID.' );
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if ( !Check::dataTitle( $contentType ) ) {
|
|
||||||
Debug::info( 'Comments: illegal Type.' );
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
$where = ['contentType', '=', $contentType, 'AND', 'contentID', '=', $contentID];
|
|
||||||
$commentData = self::$db->get( $this->tableName, $where, 'created', 'DESC', [0, $displayCount] );
|
|
||||||
if ( !$commentData->count() ) {
|
|
||||||
Debug::info( 'No comments found.' );
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return $this->filter( $commentData->results() );
|
|
||||||
}
|
|
||||||
|
|
||||||
public function update( $id, $comment ) {
|
|
||||||
if ( empty( self::$log ) ) {
|
|
||||||
self::$log = new Log;
|
|
||||||
}
|
|
||||||
if ( !Check::id( $id ) ) {
|
|
||||||
Debug::info( 'Comments: illegal ID.' );
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
$fields = [
|
|
||||||
'edited' => time(),
|
|
||||||
'content' => $comment,
|
|
||||||
'approved' => 1,
|
|
||||||
];
|
|
||||||
if ( !self::$db->update( $this->tableName, $id, $fields ) ) {
|
|
||||||
new CustomException( 'commentUpdate' );
|
|
||||||
Debug::error( "Post: $id not updated: $fields" );
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
self::$log->admin( "Updated Comment: $id" );
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function create( $contentType, $contentID, $comment ) {
|
|
||||||
if ( !Check::id( $contentID ) ) {
|
|
||||||
Debug::info( 'Comments: illegal ID.' );
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if ( !Check::dataTitle( $contentType ) ) {
|
|
||||||
Debug::info( 'Comments: illegal Type.' );
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
$fields = [
|
|
||||||
'author' => App::$activeUser->ID,
|
|
||||||
'edited' => time(),
|
|
||||||
'created' => time(),
|
|
||||||
'content' => $comment,
|
|
||||||
'contentType' => $contentType,
|
|
||||||
'contentID' => $contentID,
|
|
||||||
'approved' => 0,
|
|
||||||
];
|
|
||||||
if ( !self::$db->insert( $this->tableName, $fields ) ) {
|
|
||||||
new CustomException( 'commentCreate' );
|
|
||||||
Debug::error( "Comments: $data not created: $fields" );
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return self::$db->lastId();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function filter( $data, $params = [] ) {
|
|
||||||
foreach ( $data as $instance ) {
|
|
||||||
if ( !is_object( $instance ) ) {
|
|
||||||
$instance = $data;
|
|
||||||
$end = true;
|
|
||||||
}
|
|
||||||
if ( App::$isAdmin || ( App::$isLoggedIn && $instance->author == App::$activeUser->ID ) ) {
|
|
||||||
$instance->commentControl = Views::simpleView( 'comments.control', ['ID' => $instance->ID] );
|
|
||||||
} else {
|
|
||||||
$instance->commentControl = '';
|
|
||||||
}
|
|
||||||
$data = self::$db->get( $instance->contentType, ['ID', '=', $instance->contentID] )->results();
|
|
||||||
if ( empty( $data ) ) {
|
|
||||||
$title = 'Unknown';
|
|
||||||
} elseif ( empty( $data[0]->title ) ) {
|
|
||||||
$title = 'Unknown';
|
|
||||||
} else {
|
|
||||||
$title = $data[0]->title;
|
|
||||||
}
|
|
||||||
$authorName = self::$user->getUsername( $instance->author );
|
|
||||||
$authorAvatar = self::$user->getAvatar( $instance->author );
|
|
||||||
$instance->avatar = $authorAvatar;
|
|
||||||
$instance->authorName = $authorName;
|
|
||||||
$instance->contentTitle = $title;
|
|
||||||
$instance->content = Filters::applyOne( 'mentions.0', $instance->content, true );
|
|
||||||
$instance->content = Filters::applyOne( 'hashtags.0', $instance->content, true );
|
|
||||||
$out[] = $instance;
|
|
||||||
if ( !empty( $end ) ) {
|
|
||||||
$out = $out[0];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return $out;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function recent( $contentType = 'all', $limit = null ) {
|
|
||||||
if ( $contentType === 'all' ) {
|
|
||||||
$where = ['ID', '>', '0'];
|
|
||||||
} else {
|
|
||||||
$where = ['contentType', '=', $contentType];
|
|
||||||
}
|
|
||||||
if ( empty( $limit ) ) {
|
|
||||||
$commentData = self::$db->get( $this->tableName, $where, 'created', 'DESC' );
|
|
||||||
} else {
|
|
||||||
$commentData = self::$db->get( $this->tableName, $where, 'created', 'DESC', [0, $limit] );
|
|
||||||
}
|
|
||||||
if ( !$commentData->count() ) {
|
|
||||||
Debug::info( 'No comments found.' );
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return $this->filter( $commentData->results() );
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,154 +0,0 @@
|
|||||||
<?php
|
|
||||||
/**
|
|
||||||
* app/plugins/comments/plugin.php
|
|
||||||
*
|
|
||||||
* This houses all of the main plugin info and functionality.
|
|
||||||
*
|
|
||||||
* @package TP Comments
|
|
||||||
* @version 5.0.1
|
|
||||||
* @author Joey Kimsey <Joey@thetempusproject.com>
|
|
||||||
* @link https://TheTempusProject.com
|
|
||||||
* @license https://opensource.org/licenses/MIT [MIT LICENSE]
|
|
||||||
*/
|
|
||||||
namespace TheTempusProject\Plugins;
|
|
||||||
|
|
||||||
use ReflectionClass;
|
|
||||||
use TheTempusProject\Bedrock\Functions\Check;
|
|
||||||
use TheTempusProject\Classes\Installer;
|
|
||||||
use TheTempusProject\Houdini\Classes\Navigation;
|
|
||||||
use TheTempusProject\Classes\Plugin;
|
|
||||||
use TheTempusProject\TheTempusProject as App;
|
|
||||||
use TheTempusProject\Bedrock\Functions\Input;
|
|
||||||
use TheTempusProject\Houdini\Classes\Components;
|
|
||||||
use TheTempusProject\Houdini\Classes\Issues;
|
|
||||||
use TheTempusProject\Houdini\Classes\Views;
|
|
||||||
use TheTempusProject\Classes\Forms;
|
|
||||||
use TheTempusProject\Hermes\Functions\Redirect;
|
|
||||||
use TheTempusProject\Bedrock\Functions\Session;
|
|
||||||
use TheTempusProject\Models\Comments as CommentsModel;
|
|
||||||
|
|
||||||
class Comments extends Plugin {
|
|
||||||
protected static $comments;
|
|
||||||
public $pluginName = 'TP Comments';
|
|
||||||
public $pluginAuthor = 'JoeyK';
|
|
||||||
public $pluginWebsite = 'https://TheTempusProject.com';
|
|
||||||
public $modelVersion = '1.0';
|
|
||||||
public $pluginVersion = '3.0';
|
|
||||||
public $pluginDescription = 'A simple plugin to add user comments for other plugins.';
|
|
||||||
public $admin_links = [
|
|
||||||
[
|
|
||||||
'text' => '<i class="fa fa-fw fa-comment"></i> Comments',
|
|
||||||
'url' => '{ROOT_URL}admin/comments',
|
|
||||||
],
|
|
||||||
];
|
|
||||||
public $main_links = [
|
|
||||||
[
|
|
||||||
'text' => 'Moderator',
|
|
||||||
'url' => '{ROOT_URL}moderator/index',
|
|
||||||
'filter' => 'mod',
|
|
||||||
]
|
|
||||||
];
|
|
||||||
public $permissionMatrix = [
|
|
||||||
'modAccess' => [
|
|
||||||
'pretty' => 'Access Moderator Areas',
|
|
||||||
'default' => false,
|
|
||||||
],
|
|
||||||
];
|
|
||||||
public $resourceMatrix = [
|
|
||||||
'groups' => [
|
|
||||||
[
|
|
||||||
'name' => 'Moderator',
|
|
||||||
'permissions' => '{"adminAccess":false}',
|
|
||||||
]
|
|
||||||
],
|
|
||||||
];
|
|
||||||
|
|
||||||
public function __construct( $load = false ) {
|
|
||||||
if ( !empty(App::$activePerms) ) {
|
|
||||||
App::$isMod = !empty(App::$activePerms['modAccess']);
|
|
||||||
} else {
|
|
||||||
App::$isMod = false;
|
|
||||||
}
|
|
||||||
$this->filters[] = [
|
|
||||||
'name' => 'mod',
|
|
||||||
'find' => '#{MOD}(.*?){/MOD}#is',
|
|
||||||
'replace' => ( App::$isMod ? '$1' : '' ),
|
|
||||||
'enabled' => true,
|
|
||||||
];
|
|
||||||
self::$comments = new CommentsModel;
|
|
||||||
parent::__construct( $load );
|
|
||||||
}
|
|
||||||
|
|
||||||
public function formPost( $type, $content, $redirect ) {
|
|
||||||
if ( ! $this->checkEnabled() ) {
|
|
||||||
Debug::info( 'Comments Plugin is disabled in the control panel.' );
|
|
||||||
Issues::add( 'error', 'Comments are disabled.' );
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if ( !App::$isLoggedIn ) {
|
|
||||||
Session::flash( 'notice', 'You must be logged in to post comments.' );
|
|
||||||
return Redirect::to( $redirect . $content->ID );
|
|
||||||
}
|
|
||||||
if ( !Forms::check( 'newComment' ) ) {
|
|
||||||
Session::flash( 'error', [ 'There was a problem with your comment form.' => Check::userErrors() ] );
|
|
||||||
return Redirect::to( $redirect . $content->ID );
|
|
||||||
}
|
|
||||||
if ( !self::$comments->create( $type, $content->ID, Input::post( 'comment' ) ) ) {
|
|
||||||
Session::flash( 'error', [ 'There was a problem posting your comment.' => Check::userErrors() ] );
|
|
||||||
} else {
|
|
||||||
Session::flash( 'success', 'Comment posted' );
|
|
||||||
}
|
|
||||||
return Redirect::to( $redirect . $content->ID );
|
|
||||||
}
|
|
||||||
|
|
||||||
public function formEdit( $type, $content, $redirect ) {
|
|
||||||
if ( ! $this->checkEnabled() ) {
|
|
||||||
Debug::info( 'Comments Plugin is disabled in the control panel.' );
|
|
||||||
Issues::add( 'error', 'Comments are disabled.' );
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if ( !App::$isLoggedIn ) {
|
|
||||||
Session::flash( 'notice', 'You must be logged in to do that.' );
|
|
||||||
return Redirect::to( $type );
|
|
||||||
}
|
|
||||||
if ( !App::$isAdmin && $content->author != App::$activeUser->ID ) {
|
|
||||||
Session::flash( 'error', 'You do not have permission to edit this comment' );
|
|
||||||
return Redirect::to( $type );
|
|
||||||
}
|
|
||||||
if ( !Input::exists( 'submit' ) ) {
|
|
||||||
return Views::view( 'comments.admin.edit', $content );
|
|
||||||
}
|
|
||||||
if ( !Forms::check( 'editComment' ) ) {
|
|
||||||
Issues::add( 'error', [ 'There was a problem editing your comment.' => Check::userErrors() ] );
|
|
||||||
return Views::view( 'comments.admin.edit', $content );
|
|
||||||
}
|
|
||||||
if ( !self::$comments->update( $content->ID, Input::post( 'comment' ) ) ) {
|
|
||||||
Issues::add( 'error', [ 'There was a problem editing your comment.' => Check::userErrors() ] );
|
|
||||||
return Views::view( 'comments.admin.edit', $content );
|
|
||||||
}
|
|
||||||
Session::flash( 'success', 'Comment updated' );
|
|
||||||
return Redirect::to( $redirect . $content->contentID );
|
|
||||||
}
|
|
||||||
|
|
||||||
public function formDelete( $type, $content, $redirect ) {
|
|
||||||
if ( ! $this->checkEnabled() ) {
|
|
||||||
Debug::info( 'Comments Plugin is disabled in the control panel.' );
|
|
||||||
Issues::add( 'error', 'Comments are disabled.' );
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if ( !App::$isLoggedIn ) {
|
|
||||||
Session::flash( 'notice', 'You must be logged in to do that.' );
|
|
||||||
return Redirect::to( $type );
|
|
||||||
}
|
|
||||||
if ( !App::$isAdmin && $content->author != App::$activeUser->ID ) {
|
|
||||||
Session::flash( 'error', 'You do not have permission to edit this comment' );
|
|
||||||
return Redirect::to( $type );
|
|
||||||
}
|
|
||||||
if ( !self::$comments->delete( (array) $content->ID ) ) {
|
|
||||||
Session::flash( 'error', 'There was an error with your request.' );
|
|
||||||
} else {
|
|
||||||
Session::flash( 'success', 'Comment has been deleted' );
|
|
||||||
}
|
|
||||||
return Redirect::to( $redirect . $content->contentID );
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,28 +0,0 @@
|
|||||||
<legend>New Comments</legend>
|
|
||||||
<table class="table context-main">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th style="width: 20%"></th>
|
|
||||||
<th style="width: 70%"></th>
|
|
||||||
<th style="width: 5%"></th>
|
|
||||||
<th style="width: 5%"></th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{LOOP}
|
|
||||||
<tr>
|
|
||||||
<td>{authorName}</td>
|
|
||||||
<td>{content}</td>
|
|
||||||
<td><a href="{ROOT_URL}admin/comments/edit/{ID}" class="btn btn-sm btn-warning"><i class="fa fa-fw fa-pencil"></i></a></td>
|
|
||||||
<td><a href="{ROOT_URL}admin/comments/delete/{ID}" class="btn btn-sm btn-danger"><i class="fa fa-fw fa-trash"></i></a></td>
|
|
||||||
</tr>
|
|
||||||
{/LOOP}
|
|
||||||
{ALT}
|
|
||||||
<tr>
|
|
||||||
<td align="center" colspan="4">
|
|
||||||
No results to show.
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
{/ALT}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
@ -1,25 +0,0 @@
|
|||||||
<div class="mb-4 mt-4">
|
|
||||||
<div class="offset-md-1 col-10 p-3 context-main-bg">
|
|
||||||
<legend class="text-center">Edit Comment</legend>
|
|
||||||
<hr>
|
|
||||||
{ADMIN_BREADCRUMBS}
|
|
||||||
<form method="post" class="container py-4">
|
|
||||||
<fieldset>
|
|
||||||
<div class="mb-3 row">
|
|
||||||
<label for="comment" class="col-lg-5 col-form-label text-end">Comment:</label>
|
|
||||||
<div class="col-lg-3">
|
|
||||||
<textarea class="form-control" name="comment" maxlength="2000" rows="5" cols="50" id="comment">{content}</textarea>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Hidden Token -->
|
|
||||||
<input type="hidden" name="token" value="{TOKEN}">
|
|
||||||
|
|
||||||
<!-- Submit Button -->
|
|
||||||
<div class="text-center">
|
|
||||||
<button type="submit" name="submit" value="submit" class="btn btn-primary btn-lg">Save</button>
|
|
||||||
</div>
|
|
||||||
</fieldset>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
@ -1,45 +0,0 @@
|
|||||||
<div class="context-main-bg context-main p-3">
|
|
||||||
<legend class="text-center">Recent Comments</legend>
|
|
||||||
<hr>
|
|
||||||
{ADMIN_BREADCRUMBS}
|
|
||||||
<form action="{ROOT_URL}admin/comments/delete" method="post">
|
|
||||||
<table class="table table-striped">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th style="width: 20%">Author</th>
|
|
||||||
<th style="width: 20%">Subject</th>
|
|
||||||
<th style="width: 35%">Comment</th>
|
|
||||||
<th style="width: 10%">Time</th>
|
|
||||||
<th style="width: 5%"></th>
|
|
||||||
<th style="width: 5%"></th>
|
|
||||||
<th style="width: 5%">
|
|
||||||
<input type="checkbox" onchange="checkAll(this)" name="check.c" value="C_[]">
|
|
||||||
</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{LOOP}
|
|
||||||
<tr>
|
|
||||||
<td><a href="{ROOT_URL}admin/users/view/{author}">{authorName}</a></td>
|
|
||||||
<td><a href="{ROOT_URL}admin/blog/view/{contentID}">{contentTitle}</a></td>
|
|
||||||
<td>{content}</td>
|
|
||||||
<td>{DTC}{created}{/DTC}</td>
|
|
||||||
<td><a href="{ROOT_URL}admin/comments/edit/{ID}" class="btn btn-sm btn-warning"><i class="fa fa-fw fa-pencil"></i></a></td>
|
|
||||||
<td><a href="{ROOT_URL}admin/comments/delete/{ID}" class="btn btn-sm btn-danger"><i class="fa fa-fw fa-trash"></i></a></td>
|
|
||||||
<td>
|
|
||||||
<input type="checkbox" value="{ID}" name="C_[]">
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
{/LOOP}
|
|
||||||
{ALT}
|
|
||||||
<tr>
|
|
||||||
<td align="center" colspan="7">
|
|
||||||
No results to show.
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
{/ALT}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
<button name="submit" value="submit" type="submit" class="btn btn-sm btn-danger"><i class="fa fa-fw fa-trash"></i></button>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
@ -1,8 +0,0 @@
|
|||||||
<div class="action">
|
|
||||||
<a href="{ROOT_URL}{COMMENT_TYPE}/comments/edit/{ID}" class="btn btn-warning btn-sm">
|
|
||||||
<span class="fa fa-fw fa-pencil"></span>
|
|
||||||
</a>
|
|
||||||
<a href="{ROOT_URL}{COMMENT_TYPE}/comments/delete/{ID}" class="btn btn-danger btn-sm">
|
|
||||||
<span class="fa fa-fw fa-trash"></span>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
@ -1,14 +0,0 @@
|
|||||||
<form method="post" class="text-center mx-auto mt-4" style="max-width: 600px;">
|
|
||||||
<div class="mb-3">
|
|
||||||
<textarea
|
|
||||||
class="form-control"
|
|
||||||
name="comment"
|
|
||||||
maxlength="2000"
|
|
||||||
rows="4"
|
|
||||||
id="comment"
|
|
||||||
placeholder="Write your comment here..."></textarea>
|
|
||||||
</div>
|
|
||||||
<button name="submit" value="submit" type="submit" class="btn btn-lg btn-primary mb-3">Comment</button>
|
|
||||||
<input type="hidden" name="token" value="{TOKEN}">
|
|
||||||
<input type="hidden" name="contentId" value="{CONTENT_ID}">
|
|
||||||
</form>
|
|
@ -1,39 +0,0 @@
|
|||||||
<div class="card">
|
|
||||||
<div class="card-header d-flex align-items-center justify-content-between context-main-bg context-main">
|
|
||||||
<h3 class="card-title mb-0">
|
|
||||||
<i class="fa fa-fw fa-comment"></i> Comments
|
|
||||||
</h3>
|
|
||||||
<span class="badge bg-primary">{count}</span>
|
|
||||||
</div>
|
|
||||||
<div class="card-body context-second-bg">
|
|
||||||
<ul class="list-group list-group-flush">
|
|
||||||
{LOOP}
|
|
||||||
<li class="list-group-item context-main-bg context-main mb-2">
|
|
||||||
<div class="d-flex align-items-start">
|
|
||||||
<div class="me-3">
|
|
||||||
<img src="{ROOT_URL}{avatar}" class="rounded-circle" alt="User Avatar" style="width: 50px; height: 50px;">
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<div class="mb-1">
|
|
||||||
<small class="text-muted">
|
|
||||||
By: <a href="{ROOT_URL}home/profile/{author}" class="text-decoration-none">{authorName}</a> on {DTC date}{created}{/DTC}
|
|
||||||
</small>
|
|
||||||
</div>
|
|
||||||
<div class="comment-text">
|
|
||||||
{content}
|
|
||||||
</div>
|
|
||||||
{commentControl}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
{/LOOP}
|
|
||||||
{ALT}
|
|
||||||
<li class="list-group-item context-main-bg mb-2">
|
|
||||||
<div class="text-center">
|
|
||||||
<p class="mb-0">Be the first to comment.</p>
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
{/ALT}
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
@ -1,7 +0,0 @@
|
|||||||
<h1>Moderator' Area</h1>
|
|
||||||
<div class="jumbotron">
|
|
||||||
<h1>Welcome!</h1>
|
|
||||||
<p>This is the moderator section. You can give some groups permission to access this area. The menu is hidden for normal users and if they get a link to a moderator's area, the authentication system will stop them from accessing any content protected this way.</p>
|
|
||||||
<p>You can even use this feature in-line with your views, hiding certain components from non-moderators</p>
|
|
||||||
<p>The idea behind this role is for them to help you in policing comments as needed.</p>
|
|
||||||
</div>
|
|
@ -1,121 +0,0 @@
|
|||||||
<?php
|
|
||||||
/**
|
|
||||||
* app/controllers/messages.php
|
|
||||||
*
|
|
||||||
* This is the user messages controller.
|
|
||||||
*
|
|
||||||
* @version 5.0.1
|
|
||||||
* @author Joey Kimsey <Joey@thetempusproject.com>
|
|
||||||
* @link https://TheTempusProject.com
|
|
||||||
* @license https://opensource.org/licenses/MIT [MIT LICENSE]
|
|
||||||
*/
|
|
||||||
namespace TheTempusProject\Controllers;
|
|
||||||
|
|
||||||
use TheTempusProject\Classes\Controller;
|
|
||||||
use TheTempusProject\Classes\Forms as FormChecker;
|
|
||||||
use TheTempusProject\Models\Message;
|
|
||||||
use TheTempusProject\Houdini\Classes\Components;
|
|
||||||
use TheTempusProject\Houdini\Classes\Views;
|
|
||||||
use TheTempusProject\Houdini\Classes\Issues;
|
|
||||||
use TheTempusProject\Bedrock\Functions\Check;
|
|
||||||
use TheTempusProject\Bedrock\Functions\Input;
|
|
||||||
use TheTempusProject\TheTempusProject as App;
|
|
||||||
use TheTempusProject\Hermes\Functions\Redirect;
|
|
||||||
use TheTempusProject\Bedrock\Functions\Session;
|
|
||||||
|
|
||||||
class Messages extends Controller {
|
|
||||||
private static $message;
|
|
||||||
|
|
||||||
public function __construct() {
|
|
||||||
parent::__construct();
|
|
||||||
self::$title = 'Messages';
|
|
||||||
self::$message = new Message;
|
|
||||||
if ( ! App::$isLoggedIn ) {
|
|
||||||
Session::flash( 'error', 'You do not have permission to access this page.' );
|
|
||||||
return Redirect::home();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function create() {
|
|
||||||
self::$title .= ' - New Message';
|
|
||||||
if ( Input::get( 'prepopuser' ) ) {
|
|
||||||
$data = Input::get( 'prepopuser' );
|
|
||||||
}
|
|
||||||
if ( !empty( $data ) && self::$user->checkUsername( $data ) ) {
|
|
||||||
Components::set( 'prepopuser', $data );
|
|
||||||
} else {
|
|
||||||
Components::set( 'prepopuser', '' );
|
|
||||||
}
|
|
||||||
if ( !Input::exists( 'submit' ) ) {
|
|
||||||
return Views::view( 'messages.create' );
|
|
||||||
}
|
|
||||||
if ( !FormChecker::check( 'newMessage' ) ) {
|
|
||||||
Issues::add( 'error', [ 'There was an problem sending your messages.' => Check::userErrors() ] );
|
|
||||||
return Views::view( 'messages.create' );
|
|
||||||
}
|
|
||||||
if ( self::$message->newThread( Input::post( 'toUser' ), Input::post( 'subject' ), Input::post( 'message' ) ) ) {
|
|
||||||
Issues::add( 'success', 'Message Sent.' );
|
|
||||||
} else {
|
|
||||||
Issues::add( 'notice', 'There was an problem sending your messages.' );
|
|
||||||
}
|
|
||||||
return $this->index();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function delete( $id = '' ) {
|
|
||||||
if ( Input::exists( 'T_' ) ) {
|
|
||||||
self::$message->delete( Input::post( 'T_' ) );
|
|
||||||
}
|
|
||||||
if ( Input::exists( 'F_' ) ) {
|
|
||||||
self::$message->delete( Input::post( 'F_' ) );
|
|
||||||
}
|
|
||||||
if ( Input::exists( 'ID' ) ) {
|
|
||||||
self::$message->delete( Input::get( 'ID' ) );
|
|
||||||
}
|
|
||||||
if ( !empty( $id ) ) {
|
|
||||||
self::$message->delete( $id );
|
|
||||||
}
|
|
||||||
return $this->index();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function index() {
|
|
||||||
Components::set( 'message_inbox', Views::simpleView( 'messages.inbox', self::$message->getInbox() ) );
|
|
||||||
Components::set( 'message_outbox', Views::simpleView( 'messages.outbox', self::$message->getOutbox() ) );
|
|
||||||
Views::view( 'messages.index' );
|
|
||||||
}
|
|
||||||
|
|
||||||
public function read( $id = '' ) {
|
|
||||||
self::$message->markRead( $id );
|
|
||||||
return $this->index();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function reply() {
|
|
||||||
if ( Input::exists( 'messageID' ) ) {
|
|
||||||
$data = Input::post( 'messageID' );
|
|
||||||
}
|
|
||||||
if ( !Check::id( $data ) ) {
|
|
||||||
Issues::add( 'error', 'There was an error with your request.' );
|
|
||||||
return $this->index();
|
|
||||||
}
|
|
||||||
self::$title .= ' - Reply to: ' . self::$message->messageTitle( $data );
|
|
||||||
if ( !Input::exists( 'message' ) ) {
|
|
||||||
Components::set( 'messageID', $data );
|
|
||||||
return Views::view( 'messages.reply' );
|
|
||||||
}
|
|
||||||
if ( !FormChecker::check( 'replyMessage' ) ) {
|
|
||||||
Issues::add( 'error', [ 'There was an problem sending your messages.' => Check::userErrors() ] );
|
|
||||||
Components::set( 'messageID', $data );
|
|
||||||
return Views::view( 'messages.reply' );
|
|
||||||
}
|
|
||||||
if ( !self::$message->newMessageReply( $data, Input::post( 'message' ) ) ) {
|
|
||||||
Issues::add( 'error', 'There was an error with your request.' );
|
|
||||||
return $this->index();
|
|
||||||
}
|
|
||||||
Issues::add( 'success', 'Reply Sent.' );
|
|
||||||
return $this->index();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function view( $id = '' ) {
|
|
||||||
self::$title = self::$message->messageTitle( $id );
|
|
||||||
return Views::view( 'messages.message', self::$message->getThread( $id, true ) );
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,454 +0,0 @@
|
|||||||
<?php
|
|
||||||
/**
|
|
||||||
* app/models/message.php
|
|
||||||
*
|
|
||||||
* Houses all of the functions for the core messaging system.
|
|
||||||
*
|
|
||||||
* @version 5.0.1
|
|
||||||
* @author Joey Kimsey <Joey@thetempusproject.com>
|
|
||||||
* @link https://TheTempusProject.com
|
|
||||||
* @license https://opensource.org/licenses/MIT [MIT LICENSE]
|
|
||||||
*/
|
|
||||||
namespace TheTempusProject\Models;
|
|
||||||
|
|
||||||
use TheTempusProject\Classes\DatabaseModel;
|
|
||||||
use TheTempusProject\Houdini\Classes\Components;
|
|
||||||
use TheTempusProject\Houdini\Classes\Views;
|
|
||||||
use TheTempusProject\Canary\Bin\Canary as Debug;
|
|
||||||
use TheTempusProject\Bedrock\Functions\Check;
|
|
||||||
use TheTempusProject\Bedrock\Functions\Sanitize;
|
|
||||||
use TheTempusProject\TheTempusProject as App;
|
|
||||||
use TheTempusProject\Canary\Classes\CustomException;
|
|
||||||
|
|
||||||
class Message extends DatabaseModel {
|
|
||||||
public $tableName = 'messages';
|
|
||||||
public $databaseMatrix = [
|
|
||||||
[ 'userTo', 'int', '11' ],
|
|
||||||
[ 'userFrom', 'int', '11' ],
|
|
||||||
[ 'parent', 'int', '11' ],
|
|
||||||
[ 'sent', 'int', '10' ],
|
|
||||||
[ 'lastReply', 'int', '10' ],
|
|
||||||
[ 'senderDeleted', 'int', '1' ],
|
|
||||||
[ 'recieverDeleted', 'int', '1' ],
|
|
||||||
[ 'isRead', 'int', '1' ],
|
|
||||||
[ 'message', 'text', '' ],
|
|
||||||
[ 'subject', 'text', '' ],
|
|
||||||
];
|
|
||||||
private $messages;
|
|
||||||
private $usernames;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The model constructor.
|
|
||||||
*/
|
|
||||||
public function __construct() {
|
|
||||||
parent::__construct();
|
|
||||||
self::$message = $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieves the most recent relative message in a thread
|
|
||||||
*
|
|
||||||
* @param int $parent - the id of the parent message
|
|
||||||
* @param string $user - the id of the relative user
|
|
||||||
* @return object
|
|
||||||
*/
|
|
||||||
public function getLatestMessage( $parent, $user, $type = null ) {
|
|
||||||
if ( !Check::id( $parent ) ) {
|
|
||||||
Debug::info( 'Invalid message ID' );
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if ( !Check::id( $user ) ) {
|
|
||||||
Debug::info( 'Invalid user ID' );
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
$messageData = self::$db->get( $this->tableName, [ 'ID', '=', $parent ] );
|
|
||||||
if ( $messageData->count() == 0 ) {
|
|
||||||
Debug::info( 'Message not found.' );
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
$message = $messageData->first();
|
|
||||||
$params = [ 'parent', '=', $parent ];
|
|
||||||
if ( $type !== null ) {
|
|
||||||
$params = array_merge( $params, [ 'AND', $type, '=', $user ] );
|
|
||||||
}
|
|
||||||
$messageData = self::$db->get( $this->tableName, $params, 'ID', 'DESC', [ 0, 1 ] );
|
|
||||||
if ( $messageData->count() != 0 ) {
|
|
||||||
if ( $messageData->first()->recieverDeleted == 0 ) {
|
|
||||||
$message = $messageData->first();
|
|
||||||
} else {
|
|
||||||
$message->recieverDeleted = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return $message;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This calls a view of the requested message.
|
|
||||||
*
|
|
||||||
* @param int $ID - The message ID you are looking for.
|
|
||||||
* @return null
|
|
||||||
*/
|
|
||||||
public function getThread( $id, $markRead = false ) {
|
|
||||||
if ( !Check::id( $id ) ) {
|
|
||||||
Debug::info( 'Invalid ID' );
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
$messageData = self::$db->get( $this->tableName, [ 'ID', '=', $id ] );
|
|
||||||
if ( $messageData->count() == 0 ) {
|
|
||||||
Debug::info( 'Message not found.' );
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
$message = $messageData->first();
|
|
||||||
if ( $message->userTo == App::$activeUser->ID ) {
|
|
||||||
$permissionCheck = 1;
|
|
||||||
if ( $message->recieverDeleted == 1 ) {
|
|
||||||
Debug::info( 'User has already deleted this message.' );
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ( $message->userFrom == App::$activeUser->ID ) {
|
|
||||||
$permissionCheck = 1;
|
|
||||||
if ( $message->senderDeleted == 1 ) {
|
|
||||||
Debug::info( 'User has already deleted this message.' );
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ( empty( $permissionCheck ) ) {
|
|
||||||
Debug::info( 'You do not have permission to view this message.' );
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if ( $message->parent != 0 ) {
|
|
||||||
$find = $message->parent;
|
|
||||||
} else {
|
|
||||||
$find = $message->ID;
|
|
||||||
}
|
|
||||||
$messageData = self::$db->get( $this->tableName, [ 'ID', '=', $find, 'OR', 'Parent', '=', $find ], 'ID', 'ASC' )->results();
|
|
||||||
Components::set( 'PID', $find );
|
|
||||||
|
|
||||||
if ( $markRead == true ) {
|
|
||||||
foreach ( $messageData as $instance ) {
|
|
||||||
$this->markRead( $instance->ID );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return $this->filter( $messageData );
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getInbox( $limit = null ) {
|
|
||||||
if ( empty( $limit ) ) {
|
|
||||||
$limit = 10;
|
|
||||||
}
|
|
||||||
$limit = [ 0, $limit ];
|
|
||||||
$messageData = self::$db->get(
|
|
||||||
$this->tableName,
|
|
||||||
[
|
|
||||||
'parent', '=', 0,
|
|
||||||
'AND',
|
|
||||||
'userFrom', '=', App::$activeUser->ID,
|
|
||||||
'OR',
|
|
||||||
'parent', '=', 0,
|
|
||||||
'AND',
|
|
||||||
'userTo', '=', App::$activeUser->ID,
|
|
||||||
],
|
|
||||||
'ID',
|
|
||||||
'DESC',
|
|
||||||
$limit
|
|
||||||
);
|
|
||||||
if ( $messageData->count() == 0 ) {
|
|
||||||
Debug::info( 'getInbox: No messages found' );
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
$filters = [
|
|
||||||
'importantUser' => App::$activeUser->ID,
|
|
||||||
'deleted' => false,
|
|
||||||
'type' => 'userTo',
|
|
||||||
];
|
|
||||||
return $this->filter( $messageData->results(), $filters );
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This function calls the view for the message outbox.
|
|
||||||
*
|
|
||||||
* @return null
|
|
||||||
*/
|
|
||||||
public function getOutbox( $limit = null ) {
|
|
||||||
if ( empty( $limit ) ) {
|
|
||||||
$limit = 10;
|
|
||||||
}
|
|
||||||
$limit = [ 0, $limit ];
|
|
||||||
$messageData = self::$db->get(
|
|
||||||
$this->tableName,
|
|
||||||
[
|
|
||||||
'parent', '=', 0,
|
|
||||||
'AND',
|
|
||||||
'userFrom', '=', App::$activeUser->ID,
|
|
||||||
],
|
|
||||||
'ID',
|
|
||||||
'DESC',
|
|
||||||
$limit
|
|
||||||
);
|
|
||||||
if ( $messageData->count() == 0 ) {
|
|
||||||
Debug::info( 'getOutbox: No messages found' );
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
$filters = [
|
|
||||||
'importantUser' => App::$activeUser->ID,
|
|
||||||
'deleted' => false,
|
|
||||||
'type' => 'userFrom',
|
|
||||||
];
|
|
||||||
return $this->filter( $messageData->results(), $filters );
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This function is to prep our messages for display. An array of raw messages
|
|
||||||
* sent through this function will automatically have all the user ID's filter
|
|
||||||
* into actual usernames.
|
|
||||||
*
|
|
||||||
* @param $messageArray - This is an array of messages that need to be processed.
|
|
||||||
* @return array - It will return the same message array after being processed.
|
|
||||||
* @todo add filtering for BB-code.
|
|
||||||
*/
|
|
||||||
public function filter( $messageArray, $filters = [] ) {
|
|
||||||
$out = [];
|
|
||||||
foreach ( $messageArray as $message ) {
|
|
||||||
if ( !is_object( $message ) ) {
|
|
||||||
$message = $messageArray;
|
|
||||||
$end = true;
|
|
||||||
}
|
|
||||||
if ( isset( $filters['type'] ) && isset( $filters['importantUser'] ) ) {
|
|
||||||
$type = $filters['type'];
|
|
||||||
} else {
|
|
||||||
$type = null;
|
|
||||||
}
|
|
||||||
if ( isset( $filters['importantUser'] ) ) {
|
|
||||||
$user = $filters['importantUser'];
|
|
||||||
} else {
|
|
||||||
$user = App::$activeUser->ID;
|
|
||||||
}
|
|
||||||
if ( $message->parent == 0 ) {
|
|
||||||
$last = $this->getLatestMessage( $message->ID, $user, $type );
|
|
||||||
} else {
|
|
||||||
$last = $message;
|
|
||||||
}
|
|
||||||
if ( $type != null && $message->$type != $user && $last->$type != $user ) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if ( isset( $filters['deleted'] ) && $filters['deleted'] == false ) {
|
|
||||||
if ( $type == 'userFrom' ) {
|
|
||||||
if ( $last->senderDeleted == 1 ) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ( $type == 'userTo' ) {
|
|
||||||
if ( $last->recieverDeleted == 1 ) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$messageOut = (array) $message;
|
|
||||||
$short = explode( ' ', Sanitize::contentShort( $message->message ) );
|
|
||||||
$summary = implode( ' ', array_splice( $short, 0, 25 ) );
|
|
||||||
if ( count( $short, 1 ) >= 25 ) {
|
|
||||||
$messageOut['summary'] = $summary . '...';
|
|
||||||
} else {
|
|
||||||
$messageOut['summary'] = $summary;
|
|
||||||
}
|
|
||||||
if ( $last->isRead == 0 ) {
|
|
||||||
$messageOut['unreadBadge'] = Views::simpleView( 'messages.unreadBadge' );
|
|
||||||
} else {
|
|
||||||
$messageOut['unreadBadge'] = '';
|
|
||||||
}
|
|
||||||
$messageOut['fromAvatar'] = self::$user->getAvatar( $message->userFrom );
|
|
||||||
$messageOut['userTo'] = self::$user->getUsername( $message->userTo );
|
|
||||||
$messageOut['userToPretty'] = \ucfirst( $messageOut['userTo'] );
|
|
||||||
$messageOut['userFrom'] = self::$user->getUsername( $message->userFrom );
|
|
||||||
$messageOut['userFromPretty'] = \ucfirst( $messageOut['userFrom'] );
|
|
||||||
$out[] = (object) $messageOut;
|
|
||||||
if ( !empty( $end ) ) {
|
|
||||||
$out = $out[0];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return $out;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Function to check input and save messages to the DB.
|
|
||||||
*
|
|
||||||
* @param string $data - Username of the person receiving the sent message.
|
|
||||||
* @return function
|
|
||||||
*/
|
|
||||||
public function newThread( $to, $subject, $message ) {
|
|
||||||
if ( !self::$user->usernameExists( $to ) ) {
|
|
||||||
Debug::info( 'Message->sendMessage: User not found.' );
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
$fields = [
|
|
||||||
'userTo' => self::$user->getID( $to ),
|
|
||||||
'userFrom' => App::$activeUser->ID,
|
|
||||||
'parent' => 0,
|
|
||||||
'sent' => time(),
|
|
||||||
'lastReply' => time(),
|
|
||||||
'senderDeleted' => 0,
|
|
||||||
'recieverDeleted' => 0,
|
|
||||||
'isRead' => 0,
|
|
||||||
'subject' => $subject,
|
|
||||||
'message' => $message,
|
|
||||||
];
|
|
||||||
if ( !self::$db->insert( $this->tableName, $fields ) ) {
|
|
||||||
new CustomException( 'messageSend' );
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getUnreadCount( $userId ) {
|
|
||||||
$result = self::$db->get(
|
|
||||||
$this->tableName,
|
|
||||||
[
|
|
||||||
'userTo', '=', $userId,
|
|
||||||
'AND',
|
|
||||||
'isRead', '=', 0,
|
|
||||||
'AND',
|
|
||||||
'parent', '=', 0,
|
|
||||||
'AND',
|
|
||||||
'recieverDeleted', '=', 0,
|
|
||||||
]
|
|
||||||
);
|
|
||||||
return $result->count();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function unreadCount() {
|
|
||||||
if ( empty( App::$activeUser->ID ) ) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return $this->getUnreadCount( App::$activeUser->ID );
|
|
||||||
}
|
|
||||||
|
|
||||||
public function hasPermission( $id ) {
|
|
||||||
if ( !Check::id( $id ) ) {
|
|
||||||
Debug::info( 'Invalid ID' );
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
$messageData = self::$db->get( 'messages', [ 'ID', '=', $id ] );
|
|
||||||
if ( $messageData->count() == 0 ) {
|
|
||||||
Debug::info( 'Message not found.' );
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
$message = $messageData->first();
|
|
||||||
if ( $message->userTo != App::$activeUser->ID && $message->userFrom != App::$activeUser->ID ) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Marks a message as read. This is setup to only work
|
|
||||||
* if the message was sent to the active user.
|
|
||||||
*
|
|
||||||
* @param int - The message ID you are marking as read.
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public function markRead( $id ) {
|
|
||||||
if ( !Check::id( $id ) ) {
|
|
||||||
Debug::info( 'Invalid ID' );
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
$result = self::$db->get( $this->tableName, [ 'ID', '=', $id, 'AND', 'userTo', '=', App::$activeUser->ID, 'AND', 'isRead', '=', '0' ] );
|
|
||||||
if ( $result->count() == 0 ) {
|
|
||||||
Debug::info( 'Failed to mark message as read.' );
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if ( !self::$db->update( $this->tableName, $id, [ 'isRead' => 1 ] ) ) {
|
|
||||||
Debug::error( 'Failed to mark message as read.' );
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function newMessageReply( $id, $message ) {
|
|
||||||
if ( !$this->hasPermission( $id ) ) {
|
|
||||||
Debug::info( 'Permission Denied.' );
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
$messageData = self::$db->get( $this->tableName, [ 'ID', '=', $id ] )->first();
|
|
||||||
if ( $messageData->userTo == App::$activeUser->ID ) {
|
|
||||||
$recipient = $messageData->userFrom;
|
|
||||||
} else {
|
|
||||||
$recipient = $messageData->userTo;
|
|
||||||
}
|
|
||||||
if ( $recipient === App::$activeUser->ID ) {
|
|
||||||
Debug::info( 'Cannot send messages to yourself' );
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if ( !self::$db->update( $this->tableName, $messageData->ID, [ 'lastReply' => time() ] ) ) {
|
|
||||||
new CustomException( 'messagesReplyUpdate' );
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
$fields = [
|
|
||||||
'senderDeleted' => 0,
|
|
||||||
'recieverDeleted' => 0,
|
|
||||||
'isRead' => 0,
|
|
||||||
'userTo' => $recipient,
|
|
||||||
'userFrom' => App::$activeUser->ID,
|
|
||||||
'message' => $message,
|
|
||||||
'subject' => 're: ' . $messageData->subject,
|
|
||||||
'sent' => time(),
|
|
||||||
'parent' => $messageData->ID,
|
|
||||||
'lastReply' => time(),
|
|
||||||
];
|
|
||||||
if ( !self::$db->insert( $this->tableName, $fields ) ) {
|
|
||||||
new CustomException( 'messagesReplySend' );
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function messageTitle( $id ) {
|
|
||||||
if ( !$this->hasPermission( $id ) ) {
|
|
||||||
Debug::info( 'Permission Denied.' );
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
$message = self::$db->get( $this->tableName, [ 'ID', '=', $id ] )->first();
|
|
||||||
return $message->subject;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Function to delete messages from the DB.
|
|
||||||
*
|
|
||||||
* @param int $data - The ID of the message you are trying to delete.
|
|
||||||
* @todo - done at 5 am after no sleep. This can be simplified a lot, i just wanted a working solution ASAP
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public function delete( $data ) {
|
|
||||||
if ( !is_array( $data ) ) {
|
|
||||||
$data = [ $data ];
|
|
||||||
}
|
|
||||||
foreach ( $data as $instance ) {
|
|
||||||
if ( !Check::id( $instance ) ) {
|
|
||||||
$error = true;
|
|
||||||
}
|
|
||||||
if ( !$this->hasPermission( $instance ) ) {
|
|
||||||
Debug::info( 'Permission Denied.' );
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
$message = self::$db->get( $this->tableName, [ 'ID', '=', $instance ] )->first();
|
|
||||||
if ( $message->userTo == App::$activeUser->ID ) {
|
|
||||||
$fields = [ 'recieverDeleted' => '1' ];
|
|
||||||
} else {
|
|
||||||
$fields = [ 'senderDeleted' => '1' ];
|
|
||||||
}
|
|
||||||
if ( !self::$db->update( $this->tableName, $instance, $fields ) ) {
|
|
||||||
$error = true;
|
|
||||||
}
|
|
||||||
Debug::info( "message Deleted: $instance" );
|
|
||||||
if ( !empty( $end ) ) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ( !empty( $error ) ) {
|
|
||||||
Debug::info( 'There was an error deleting one or more messages.' );
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,57 +0,0 @@
|
|||||||
<?php
|
|
||||||
/**
|
|
||||||
* app/plugins/messages/plugin.php
|
|
||||||
*
|
|
||||||
* This houses all of the main plugin info and functionality.
|
|
||||||
*
|
|
||||||
* @package TP Messages
|
|
||||||
* @version 5.0.1
|
|
||||||
* @author Joey Kimsey <Joey@thetempusproject.com>
|
|
||||||
* @link https://TheTempusProject.com
|
|
||||||
* @license https://opensource.org/licenses/MIT [MIT LICENSE]
|
|
||||||
*/
|
|
||||||
namespace TheTempusProject\Plugins;
|
|
||||||
|
|
||||||
use TheTempusProject\TheTempusProject as App;
|
|
||||||
use TheTempusProject\Classes\Plugin;
|
|
||||||
use TheTempusProject\Models\Message;
|
|
||||||
use TheTempusProject\Houdini\Classes\Components;
|
|
||||||
use TheTempusProject\Houdini\Classes\Views;
|
|
||||||
|
|
||||||
class Messages extends Plugin {
|
|
||||||
public $pluginName = 'TP Messages';
|
|
||||||
public $pluginAuthor = 'JoeyK';
|
|
||||||
public $pluginWebsite = 'https://TheTempusProject.com';
|
|
||||||
public $modelVersion = '1.0';
|
|
||||||
public $pluginVersion = '3.0';
|
|
||||||
public $pluginDescription = 'A simple plugin which adds a site wide messaging system.';
|
|
||||||
public $permissionMatrix = [
|
|
||||||
'sendMessages' => [
|
|
||||||
'pretty' => 'Can send Messages',
|
|
||||||
'default' => false,
|
|
||||||
],
|
|
||||||
];
|
|
||||||
private static $loaded = false;
|
|
||||||
|
|
||||||
public function __construct() {
|
|
||||||
if ( ! self::$loaded ) {
|
|
||||||
$messages = new Message;
|
|
||||||
Components::set( 'MESSAGE_COUNT', $messages->unreadCount() );
|
|
||||||
if ( $messages->unreadCount() > 0 ) {
|
|
||||||
$messageBadge = Views::simpleView( 'messages.badge' );
|
|
||||||
} else {
|
|
||||||
$messageBadge = '';
|
|
||||||
}
|
|
||||||
Components::set( 'MBADGE', $messageBadge );
|
|
||||||
if ( App::$isLoggedIn ) {
|
|
||||||
Components::set( 'RECENT_MESSAGES', Views::simpleView( 'messages.nav.recentMessagesDropdown', $messages->getInbox( 5 ) ) );
|
|
||||||
} else {
|
|
||||||
Components::set( 'RECENT_MESSAGES', '' );
|
|
||||||
}
|
|
||||||
App::$topNavRight .= '{RECENT_MESSAGES}';
|
|
||||||
App::$topNavRightDropdown .= '<li><a href="{ROOT_URL}messages" class="dropdown-item"><i class="fa fa-fw fa-envelope"></i> Inbox {MBADGE}</a></li>';
|
|
||||||
self::$loaded = true;
|
|
||||||
}
|
|
||||||
parent::__construct();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1 +0,0 @@
|
|||||||
<span class="badge bg-danger rounded-pill">{MESSAGE_COUNT}</span>
|
|
@ -1,73 +0,0 @@
|
|||||||
<div class="m-2 m-lg-4">
|
|
||||||
<div class="col-12 mx-5 col-sm-10 col-lg-8 mx-auto p-4 rounded shadow-sm context-main-bg">
|
|
||||||
<nav aria-label="breadcrumb">
|
|
||||||
<ol class="breadcrumb">
|
|
||||||
<li class="breadcrumb-item">
|
|
||||||
<a href="{ROOT_URL}messages" class="text-decoration-none">
|
|
||||||
Messages
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li class="breadcrumb-item active" aria-current="page">
|
|
||||||
Create
|
|
||||||
</li>
|
|
||||||
</ol>
|
|
||||||
</nav>
|
|
||||||
<div class="row justify-content-center">
|
|
||||||
<div class="col-md-6 col-lg-6">
|
|
||||||
<form method="post" class="needs-validation">
|
|
||||||
<legend class="mb-3">New Message</legend>
|
|
||||||
<fieldset>
|
|
||||||
<!-- To User Field -->
|
|
||||||
<div class="mb-3 row">
|
|
||||||
<label for="toUser" class="col-sm-6 col-form-label">To:</label>
|
|
||||||
<div class="col-sm-6">
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
class="form-control"
|
|
||||||
name="toUser"
|
|
||||||
id="toUser"
|
|
||||||
value="{prepopuser}"
|
|
||||||
required>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Subject Field -->
|
|
||||||
<div class="mb-3 row">
|
|
||||||
<label for="subject" class="col-sm-6 col-form-label">Subject:</label>
|
|
||||||
<div class="col-sm-6">
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
class="form-control"
|
|
||||||
name="subject"
|
|
||||||
id="subject"
|
|
||||||
required>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Message Field -->
|
|
||||||
<div class="mb-3">
|
|
||||||
<label for="message" class="form-label">Message:</label>
|
|
||||||
<textarea
|
|
||||||
class="form-control"
|
|
||||||
name="message"
|
|
||||||
id="message"
|
|
||||||
rows="6"
|
|
||||||
maxlength="2000"
|
|
||||||
required></textarea>
|
|
||||||
</div>
|
|
||||||
</fieldset>
|
|
||||||
<input type="hidden" name="token" value="{TOKEN}">
|
|
||||||
<div class="text-center text-md-start">
|
|
||||||
<button
|
|
||||||
type="submit"
|
|
||||||
name="submit"
|
|
||||||
value="submit"
|
|
||||||
class="btn btn-primary btn-lg">
|
|
||||||
Send
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
@ -1,40 +0,0 @@
|
|||||||
<h2>Inbox</h2>
|
|
||||||
<form action="{ROOT_URL}messages/delete" method="post">
|
|
||||||
<table class="table table-striped text-center">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th style="width: 15%">From</th>
|
|
||||||
<th style="width: 40%">Subject</th>
|
|
||||||
<th style="width: 20%">Last Reply</th>
|
|
||||||
<th style="width: 10%"></th>
|
|
||||||
<th style="width: 10%"></th>
|
|
||||||
<th style="width: 5%">
|
|
||||||
<INPUT type="checkbox" onchange="checkAll(this)" name="check.t" value="T_[]"/>
|
|
||||||
</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{LOOP}
|
|
||||||
<tr {unreadBadge}>
|
|
||||||
<td><a href="{ROOT_URL}home/profile/{userFrom}" class="text-decoration-none">{userFromPretty}</a></td>
|
|
||||||
<td><a href="{ROOT_URL}messages/view/{ID}" class="text-decoration-none">{subject}</a></td>
|
|
||||||
<td>{DTC date}{lastReply}{/DTC}</td>
|
|
||||||
<td><a href="{ROOT_URL}messages/read/{ID}" class="btn btn-sm btn-info"><i class="fa-solid fa-envelope-open"></i></a></td>
|
|
||||||
<td><a href="{ROOT_URL}messages/delete/{ID}" class="btn btn-sm btn-danger"><i class="fa fa-fw fa-trash"></i></a></td>
|
|
||||||
<td>
|
|
||||||
<input type="checkbox" value="{ID}" name="T_[]">
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
{/LOOP}
|
|
||||||
{ALT}
|
|
||||||
<tr>
|
|
||||||
<td align="center" colspan="6">
|
|
||||||
No Messages.
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
{/ALT}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
<button name="submit" value="submit" type="submit" class="btn btn-sm btn-danger"><i class="fa fa-fw fa-trash"></i></button>
|
|
||||||
<a href="{ROOT_URL}messages/create" class="btn btn-sm btn-primary">New message</a>
|
|
||||||
</form>
|
|
@ -1,10 +0,0 @@
|
|||||||
<div class="m-2 m-lg-4">
|
|
||||||
<div class="col-12 col-sm-10 col-lg-8 mx-auto p-md-2 p-lg-4 rounded shadow-sm context-main-bg context-main">
|
|
||||||
<div class="my-3 p-3">
|
|
||||||
{message_inbox}
|
|
||||||
</div>
|
|
||||||
<div class="my-3 p-3">
|
|
||||||
{message_outbox}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
@ -1,53 +0,0 @@
|
|||||||
<div class="m-2 m-lg-4">
|
|
||||||
<div class="col-12 mx-5 col-sm-10 col-lg-8 mx-auto p-4 rounded shadow-sm context-main-bg context-main">
|
|
||||||
<nav aria-label="breadcrumb">
|
|
||||||
<ol class="breadcrumb">
|
|
||||||
<li class="breadcrumb-item">
|
|
||||||
<a href="{ROOT_URL}messages" class="text-decoration-none">
|
|
||||||
Messages
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li class="breadcrumb-item active" aria-current="page">
|
|
||||||
View
|
|
||||||
</li>
|
|
||||||
</ol>
|
|
||||||
</nav>
|
|
||||||
<div class="card panel-primary col-lg-8 mx-auto text-center">
|
|
||||||
{LOOP}
|
|
||||||
{SINGLE}
|
|
||||||
<div class="card-header context-second-bg">
|
|
||||||
<p class="card-title context-main text-center h3">{subject}</p>
|
|
||||||
</div>
|
|
||||||
{/SINGLE}
|
|
||||||
<div class="card-body context-other-bg">
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-4 col-lg-3 text-center">
|
|
||||||
<a href="{ROOT_URL}home/profile/{userFrom}" class="text-decoration-none">{userFromPretty}</a><br>
|
|
||||||
<img alt="User Pic" src="{ROOT_URL}{fromAvatar}" class="img-circle img-fluid mt-2">
|
|
||||||
</div>
|
|
||||||
<div class="col-8 col-lg-9 text-start">
|
|
||||||
{message}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="card-footer context-second-bg">
|
|
||||||
{ADMIN}
|
|
||||||
<div class="d-flex justify-content-between">
|
|
||||||
<div class="">
|
|
||||||
{ID}
|
|
||||||
</div>
|
|
||||||
<div class="">
|
|
||||||
{DTC}{sent}{/DTC}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{/ADMIN}
|
|
||||||
</div>
|
|
||||||
{/LOOP}
|
|
||||||
</div>
|
|
||||||
<form action="{ROOT_URL}messages/reply" method="post">
|
|
||||||
<input type="hidden" name="token" value="{TOKEN}">
|
|
||||||
<input type="hidden" name="messageID" value="{PID}">
|
|
||||||
<button name="submit" value="reply" type="submit" class="btn btn-md btn-primary my-4">Reply</button>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
@ -1,46 +0,0 @@
|
|||||||
<div class="dropdown nav-item mx-2 my-2 my-lg-0">
|
|
||||||
<a
|
|
||||||
href="#"
|
|
||||||
class="d-flex align-items-center text-white text-decoration-none dropdown-toggle"
|
|
||||||
id="messagesDropdown"
|
|
||||||
data-bs-toggle="dropdown"
|
|
||||||
aria-haspopup="true"
|
|
||||||
aria-expanded="false">
|
|
||||||
<i class="fa fa-fw fa-envelope"></i><span class="">{MBADGE}</span>
|
|
||||||
</a>
|
|
||||||
<ul class="dropdown-menu dropdown-menu-dark dropdown-menu-end text-small shadow" aria-labelledby="messagesDropdown">
|
|
||||||
{LOOP}
|
|
||||||
<!-- Message Item -->
|
|
||||||
<li>
|
|
||||||
<a href="{ROOT_URL}messages/view/{ID}" class="dropdown-item">
|
|
||||||
<div class="d-flex">
|
|
||||||
<h5 class="media-heading text-start">
|
|
||||||
<img class="" style="width: 40px;" src="{ROOT_URL}{fromAvatar}" alt="">
|
|
||||||
<strong>{userFrom}</strong>
|
|
||||||
</h5>
|
|
||||||
<div class="text-end">
|
|
||||||
<div class="media-body">
|
|
||||||
<p class="small text-muted mb-1"><i class="fa fa-clock-o me-1"></i> {DTC}{lastReply}{/DTC}</p>
|
|
||||||
<span>{summary}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
{/LOOP}
|
|
||||||
{ALT}
|
|
||||||
<li class="px-3 text-center">
|
|
||||||
<strong>No Messages</strong>
|
|
||||||
</li>
|
|
||||||
{/ALT}
|
|
||||||
<!-- Footer -->
|
|
||||||
<li>
|
|
||||||
<hr class="dropdown-divider">
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a href="/messages" class="dropdown-item text-center">
|
|
||||||
Read All New Messages
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
@ -1,37 +0,0 @@
|
|||||||
<h2>Outbox</h2>
|
|
||||||
<form action="{ROOT_URL}messages/delete" method="post">
|
|
||||||
<table class="table table-striped text-center">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th style="width: 20%">To</th>
|
|
||||||
<th style="width: 40%">Subject</th>
|
|
||||||
<th style="width: 20%">Sent</th>
|
|
||||||
<th style="width: 10%"></th>
|
|
||||||
<th style="width: 10%">
|
|
||||||
<INPUT type="checkbox" onchange="checkAll(this)" name="check.e" value="F_[]"/>
|
|
||||||
</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{LOOP}
|
|
||||||
<tr>
|
|
||||||
<td><a href="{ROOT_URL}home/profile/{userTo}" class="text-decoration-none">{userToPretty}</a></td>
|
|
||||||
<td><a href="{ROOT_URL}messages/view/{ID}" class="text-decoration-none">{subject}</a></td>
|
|
||||||
<td>{DTC date}{sent}{/DTC}</td>
|
|
||||||
<td><a href="{ROOT_URL}messages/delete/{ID}" class="btn btn-sm btn-danger"><i class="fa fa-fw fa-trash"></i></a></td>
|
|
||||||
<td>
|
|
||||||
<input type="checkbox" value="{ID}" name="F_[]">
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
{/LOOP}
|
|
||||||
{ALT}
|
|
||||||
<tr>
|
|
||||||
<td align="center" colspan="6">
|
|
||||||
No Messages
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
{/ALT}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
<button name="submit" value="submit" type="submit" class="btn btn-sm btn-danger"><i class="fa fa-fw fa-trash"></i></button>
|
|
||||||
</form>
|
|
@ -1,18 +0,0 @@
|
|||||||
<div class="context-main context-main-bg col-8 offset-2 my-3 p-3">
|
|
||||||
<form method="post">
|
|
||||||
<legend class="text-center">Reply</legend>
|
|
||||||
<fieldset>
|
|
||||||
<div class="form-group">
|
|
||||||
<div class="col-6 offset-3">
|
|
||||||
<label for="message" class="control-label">Message:</label>
|
|
||||||
<textarea class="form-control" name="message" maxlength="2000" rows="10" cols="50" id="message"></textarea>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</fieldset>
|
|
||||||
<input type="hidden" name="messageID" value="{messageID}">
|
|
||||||
<input type="hidden" name="token" value="{TOKEN}">
|
|
||||||
<div class="text-center">
|
|
||||||
<button name="submit" value="submit" type="submit" class="btn btn-lg btn-primary center-block my-3">Send</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
@ -1 +0,0 @@
|
|||||||
class="bg-info"
|
|
@ -1,85 +0,0 @@
|
|||||||
<?php
|
|
||||||
/**
|
|
||||||
* app/plugins/notifications/controllers/admin/notifications.php
|
|
||||||
*
|
|
||||||
* This is the notifications admin controller.
|
|
||||||
*
|
|
||||||
* @package TP Notifications
|
|
||||||
* @version 5.0.1
|
|
||||||
* @author Joey Kimsey <Joey@thetempusproject.com>
|
|
||||||
* @link https://TheTempusProject.com
|
|
||||||
* @license https://opensource.org/licenses/MIT [MIT LICENSE]
|
|
||||||
*/
|
|
||||||
namespace TheTempusProject\Controllers\Admin;
|
|
||||||
|
|
||||||
use TheTempusProject\Bedrock\Functions\Input;
|
|
||||||
use TheTempusProject\Houdini\Classes\Issues;
|
|
||||||
use TheTempusProject\Houdini\Classes\Views;
|
|
||||||
use TheTempusProject\Houdini\Classes\Navigation;
|
|
||||||
use TheTempusProject\Houdini\Classes\Components;
|
|
||||||
use TheTempusProject\Classes\AdminController;
|
|
||||||
use TheTempusProject\Models\Notification as NotificationsModel;
|
|
||||||
use TheTempusProject\Models\Group;
|
|
||||||
use TheTempusProject\Models\User;
|
|
||||||
use TheTempusProject\Houdini\Classes\Forms;
|
|
||||||
use TheTempusProject\Classes\Forms as FormChecker;
|
|
||||||
|
|
||||||
class Notifications extends AdminController {
|
|
||||||
protected static $notifications;
|
|
||||||
public static $user;
|
|
||||||
public static $group;
|
|
||||||
|
|
||||||
public function __construct() {
|
|
||||||
parent::__construct();
|
|
||||||
self::$title = 'Admin - Notifications';
|
|
||||||
self::$notifications = new NotificationsModel;
|
|
||||||
self::$user = new User;
|
|
||||||
self::$group = new Group;
|
|
||||||
$view = Navigation::activePageSelect( 'nav.admin', '/admin/Notifications' );
|
|
||||||
Components::set( 'ADMINNAV', $view );
|
|
||||||
}
|
|
||||||
|
|
||||||
public function index( $data = null ) {
|
|
||||||
// this is just a simple form to allow admins to send notifications
|
|
||||||
return $this->create();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function create() {
|
|
||||||
$select = Forms::getSelectHtml(
|
|
||||||
'groupSelect',
|
|
||||||
self::$group->listGroupsSimple( true )
|
|
||||||
);
|
|
||||||
Components::set( 'groupSelect', $select );
|
|
||||||
if ( ! Input::exists( 'submit' ) ) {
|
|
||||||
return Views::view( 'notifications.admin.send' );
|
|
||||||
}
|
|
||||||
if ( ! FormChecker::check( 'createNotification' ) ) {
|
|
||||||
Issues::add( 'error', [ 'There was an error with your request.' => Check::userErrors() ] );
|
|
||||||
return Views::view( 'notifications.admin.send' );
|
|
||||||
}
|
|
||||||
if ( Input::exists( 'expires' ) ) {
|
|
||||||
$expiresAt = time() + intval( Input::post( 'expires' ) );
|
|
||||||
} else {
|
|
||||||
$expiresAt = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( Input::exists( 'groupSelect' ) && Input::post( 'groupSelect' ) != 0 && Input::post( 'groupSelect' ) != 'all' ) {
|
|
||||||
$list = self::$group->listMembers( Input::post( 'groupSelect' ) );
|
|
||||||
} else {
|
|
||||||
$list = self::$user->userList();
|
|
||||||
}
|
|
||||||
|
|
||||||
$results = [];
|
|
||||||
foreach ( $list as $recipient ) {
|
|
||||||
$results[] = self::$notifications->create( Input::post( 'notification' ), 'Admin Issued', $recipient->ID, $expiresAt );
|
|
||||||
}
|
|
||||||
|
|
||||||
// $result = self::$notifications->create( Input::post( 'notification' ), 'Admin Issued', XXXXXXX_user_idsXXXXXX, $expiresAt );
|
|
||||||
if ( ! empty( $results ) ) {
|
|
||||||
Issues::add( 'success', 'Your notification has been sent.' );
|
|
||||||
} else {
|
|
||||||
Issues::add( 'error', [ 'There was an unknown error submitting your data.' => Check::userErrors() ] );
|
|
||||||
}
|
|
||||||
Views::view( 'notifications.admin.send' );
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,79 +0,0 @@
|
|||||||
<?php
|
|
||||||
/**
|
|
||||||
* app/plugins/notifications/controllers/notifications.php
|
|
||||||
*
|
|
||||||
* This is the home controller for the notifications plugin.
|
|
||||||
*
|
|
||||||
* @package TP Notifications
|
|
||||||
* @version 5.0.1
|
|
||||||
* @author Joey Kimsey <Joey@thetempusproject.com>
|
|
||||||
* @link https://TheTempusProject.com
|
|
||||||
* @license https://opensource.org/licenses/MIT [MIT LICENSE]
|
|
||||||
*/
|
|
||||||
namespace TheTempusProject\Controllers;
|
|
||||||
|
|
||||||
use TheTempusProject\Houdini\Classes\Views;
|
|
||||||
use TheTempusProject\Houdini\Classes\Issues;
|
|
||||||
use TheTempusProject\Classes\Controller;
|
|
||||||
use TheTempusProject\Models\Notification as NotificationsModel;
|
|
||||||
use TheTempusProject\TheTempusProject as App;
|
|
||||||
use TheTempusProject\Hermes\Functions\Redirect;
|
|
||||||
use TheTempusProject\Bedrock\Functions\Session;
|
|
||||||
|
|
||||||
class Notifications extends Controller {
|
|
||||||
protected static $notifications;
|
|
||||||
|
|
||||||
public function __construct() {
|
|
||||||
parent::__construct();
|
|
||||||
self::$notifications = new NotificationsModel;
|
|
||||||
self::$title = 'Notifications - {SITENAME}';
|
|
||||||
self::$pageDescription = 'Your recent notifications';
|
|
||||||
if ( ! App::$isLoggedIn ) {
|
|
||||||
Session::flash( 'error', 'You do not have permission to access this page.' );
|
|
||||||
return Redirect::home();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function index() {
|
|
||||||
$notifications = self::$notifications->getByUser( 10 );
|
|
||||||
Views::view( 'notifications.list', $notifications );
|
|
||||||
}
|
|
||||||
|
|
||||||
public function markRead( $id = null ) {
|
|
||||||
$notification = self::$notifications->findById( $id );
|
|
||||||
if ( $notification == false ) {
|
|
||||||
Issues::add( 'error', 'Notification not found.' );
|
|
||||||
return $this->index();
|
|
||||||
}
|
|
||||||
if ( $notification->userID != App::$activeUser->ID ) {
|
|
||||||
Issues::add( 'error', 'You do not have permission to modify this notification.' );
|
|
||||||
return $this->index();
|
|
||||||
}
|
|
||||||
$result = self::$notifications->markSeen( $id );
|
|
||||||
if ( $result == true ) {
|
|
||||||
Issues::add( 'success', 'Notification marked as read.' );
|
|
||||||
} else {
|
|
||||||
Issues::add( 'notice', 'There was an problem updating your notification.' );
|
|
||||||
}
|
|
||||||
return $this->index();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function delete( $id = null ) {
|
|
||||||
$notification = self::$notifications->findById( $id );
|
|
||||||
if ( $notification == false ) {
|
|
||||||
Issues::add( 'error', 'Notification not found.' );
|
|
||||||
return $this->index();
|
|
||||||
}
|
|
||||||
if ( $notification->userID != App::$activeUser->ID ) {
|
|
||||||
Issues::add( 'error', 'You do not have permission to modify this notification.' );
|
|
||||||
return $this->index();
|
|
||||||
}
|
|
||||||
$result = self::$notifications->delete( $id );
|
|
||||||
if ( $result == true ) {
|
|
||||||
Issues::add( 'success', 'Notification deleted.' );
|
|
||||||
} else {
|
|
||||||
Issues::add( 'notice', 'There was an problem deleting your notification.' );
|
|
||||||
}
|
|
||||||
return $this->index();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,146 +0,0 @@
|
|||||||
<?php
|
|
||||||
/**
|
|
||||||
* app/plugins/notifications/models/notification.php
|
|
||||||
*
|
|
||||||
* This class is used for the manipulation of the notifications database table.
|
|
||||||
*
|
|
||||||
* @package TP Notifications
|
|
||||||
* @version 5.0.1
|
|
||||||
* @author Joey Kimsey <Joey@thetempusproject.com>
|
|
||||||
* @link https://TheTempusProject.com
|
|
||||||
* @license https://opensource.org/licenses/MIT [MIT LICENSE]
|
|
||||||
*/
|
|
||||||
namespace TheTempusProject\Models;
|
|
||||||
|
|
||||||
use TheTempusProject\Bedrock\Functions\Check;
|
|
||||||
use TheTempusProject\Canary\Bin\Canary as Debug;
|
|
||||||
use TheTempusProject\Classes\DatabaseModel;
|
|
||||||
use TheTempusProject\TheTempusProject as App;
|
|
||||||
use TheTempusProject\Houdini\Classes\Views;
|
|
||||||
use TheTempusProject\Canary\Classes\CustomException;
|
|
||||||
|
|
||||||
class Notification extends DatabaseModel {
|
|
||||||
public $tableName = 'notifications';
|
|
||||||
public $databaseMatrix = [
|
|
||||||
[ 'notification', 'text', '' ],
|
|
||||||
[ 'origin', 'varchar', '128' ],
|
|
||||||
[ 'userID', 'int', '11' ],
|
|
||||||
[ 'createdAt', 'int', '11' ],
|
|
||||||
[ 'expiresAt', 'int', '11' ],
|
|
||||||
[ 'deletedAt', 'int', '11' ],
|
|
||||||
[ 'seenAt', 'int', '11' ],
|
|
||||||
];
|
|
||||||
|
|
||||||
public function getUnreadCount( $userID ) {
|
|
||||||
$result = self::$db->get(
|
|
||||||
$this->tableName,
|
|
||||||
[
|
|
||||||
'userID', '=', $userID,
|
|
||||||
'AND',
|
|
||||||
'deletedAt', '=', '0',
|
|
||||||
'AND',
|
|
||||||
'seenAt', '=', '0',
|
|
||||||
'AND',
|
|
||||||
'expiresAt', '<', time(),
|
|
||||||
]
|
|
||||||
);
|
|
||||||
return $result->count();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function create( $notification, $origin, $userID, $expiresAt = '0', $deletedAt = '0', $seenAt = '0' ) {
|
|
||||||
$fields = [
|
|
||||||
'notification' => $notification,
|
|
||||||
'origin' => $origin,
|
|
||||||
'userID' => $userID,
|
|
||||||
'createdAt' => time(),
|
|
||||||
'expiresAt' => $expiresAt,
|
|
||||||
'deletedAt' => $deletedAt,
|
|
||||||
'seenAt' => $seenAt,
|
|
||||||
];
|
|
||||||
if ( !self::$db->insert( $this->tableName, $fields ) ) {
|
|
||||||
Debug::info( 'Events::create - failed to insert to db' );
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return self::$db->lastId();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getByUser( $limit = 0 ) {
|
|
||||||
$whereClause = [
|
|
||||||
'userID', '=', App::$activeUser->ID,
|
|
||||||
'AND',
|
|
||||||
'deletedAt', '=', '0',
|
|
||||||
'AND',
|
|
||||||
'expiresAt', '<', time(),
|
|
||||||
];
|
|
||||||
if ( empty( $limit ) ) {
|
|
||||||
$notifications = self::$db->get( $this->tableName, $whereClause );
|
|
||||||
} else {
|
|
||||||
$notifications = self::$db->get( $this->tableName, $whereClause, 'ID', 'DESC', [0, $limit] );
|
|
||||||
}
|
|
||||||
if ( !$notifications->count() ) {
|
|
||||||
Debug::info( 'Notification:getByUser No Notifications found' );
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return $this->filter( $notifications->results() );
|
|
||||||
}
|
|
||||||
|
|
||||||
public function markSeen( $id ) {
|
|
||||||
if ( !Check::id( $id ) ) {
|
|
||||||
Debug::info( 'Notifications: illegal ID.' );
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
$fields = [
|
|
||||||
'seenAt' => time(),
|
|
||||||
];
|
|
||||||
$notification = self::findById( $id );
|
|
||||||
if ( ! $notification ) {
|
|
||||||
Debug::error( "Notifications: $id not updated" );
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if ( !self::$db->update( $this->tableName, $id, $fields ) ) {
|
|
||||||
new CustomException( 'notificationUpdate' );
|
|
||||||
Debug::error( "Notifications: $id not updated" );
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function delete( $id ) {
|
|
||||||
if ( !Check::id( $id ) ) {
|
|
||||||
Debug::info( 'Notifications: illegal ID.' );
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
$fields = [
|
|
||||||
'deletedAt' => time(),
|
|
||||||
];
|
|
||||||
if ( !self::$db->update( $this->tableName, $id, $fields ) ) {
|
|
||||||
new CustomException( 'notificationDelete' );
|
|
||||||
Debug::error( "Notifications: $id not updated" );
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function filter( $messageArray, $filters = [] ) {
|
|
||||||
$out = [];
|
|
||||||
foreach ( $messageArray as $message ) {
|
|
||||||
if ( !is_object( $message ) ) {
|
|
||||||
$message = $messageArray;
|
|
||||||
$end = true;
|
|
||||||
}
|
|
||||||
if ( $message->seenAt == 0 ) {
|
|
||||||
$message->unseenBadge = Views::simpleView( 'notifications.unseenBadge' );
|
|
||||||
$message->markReadLink = '<a href="{ROOT_URL}notifications/markRead/'.$message->ID.'" class="btn btn-sm btn-primary"><i class="fa-solid fa-fw fa-envelope-open"></i></a>';
|
|
||||||
} else {
|
|
||||||
$message->unseenBadge = '';
|
|
||||||
$message->markReadLink = '';
|
|
||||||
}
|
|
||||||
$out[] = (object) $message;
|
|
||||||
if ( !empty( $end ) ) {
|
|
||||||
$out = $out[0];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return $out;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,64 +0,0 @@
|
|||||||
<?php
|
|
||||||
/**
|
|
||||||
* app/plugins/notifications/plugin.php
|
|
||||||
*
|
|
||||||
* This houses all of the main plugin info and functionality.
|
|
||||||
*
|
|
||||||
* @package TP Notifications
|
|
||||||
* @version 5.0.1
|
|
||||||
* @author Joey Kimsey <Joey@thetempusproject.com>
|
|
||||||
* @link https://TheTempusProject.com
|
|
||||||
* @license https://opensource.org/licenses/MIT [MIT LICENSE]
|
|
||||||
*/
|
|
||||||
namespace TheTempusProject\Plugins;
|
|
||||||
|
|
||||||
use TheTempusProject\TheTempusProject as App;
|
|
||||||
use TheTempusProject\Classes\Plugin;
|
|
||||||
use TheTempusProject\Models\Notification;
|
|
||||||
use TheTempusProject\Houdini\Classes\Components;
|
|
||||||
use TheTempusProject\Houdini\Classes\Views;
|
|
||||||
|
|
||||||
class Notifications extends Plugin {
|
|
||||||
public $pluginName = 'TP Notifications';
|
|
||||||
public $pluginAuthor = 'JoeyK';
|
|
||||||
public $pluginWebsite = 'https://TheTempusProject.com';
|
|
||||||
public $modelVersion = '1.0';
|
|
||||||
public $pluginVersion = '3.0';
|
|
||||||
public $pluginDescription = 'A simple plugin which adds a site wide notification system.';
|
|
||||||
public $permissionMatrix = [
|
|
||||||
'sendNotifications' => [
|
|
||||||
'pretty' => 'Can send notifications',
|
|
||||||
'default' => false,
|
|
||||||
],
|
|
||||||
];
|
|
||||||
public $admin_links = [
|
|
||||||
[
|
|
||||||
'text' => '<i class="fa fa-fw fa-bell"></i> Notify',
|
|
||||||
'url' => '{ROOT_URL}admin/notifications',
|
|
||||||
],
|
|
||||||
];
|
|
||||||
private static $loaded = false;
|
|
||||||
public function __construct( $load = false ) {
|
|
||||||
parent::__construct( $load );
|
|
||||||
if ( $this->checkEnabled() && App::$isLoggedIn ) {
|
|
||||||
$notifications = new Notification;
|
|
||||||
Components::set( 'notificationCount', $notifications->getUnreadCount( App::$activeUser->ID ) );
|
|
||||||
if ( $notifications->getUnreadCount( App::$activeUser->ID ) > 0 ) {
|
|
||||||
$messageBadge = Views::simpleView( 'notifications.badge' );
|
|
||||||
} else {
|
|
||||||
$messageBadge = '';
|
|
||||||
}
|
|
||||||
Components::set( 'NBADGE', $messageBadge );
|
|
||||||
if ( ! self::$loaded ) {
|
|
||||||
if ( App::$isLoggedIn ) {
|
|
||||||
Components::set( 'recentNotifications', Views::simpleView( 'notifications.nav.recentNotificationsDropdown', $notifications->getByUser( 10 ) ) );
|
|
||||||
} else {
|
|
||||||
Components::set( 'recentNotifications', '' );
|
|
||||||
}
|
|
||||||
App::$topNavRight .= '{recentNotifications}';
|
|
||||||
App::$topNavRightDropdown .= '<li><a href="{ROOT_URL}notifications" class="dropdown-item"><i class="fa fa-fw fa-bell"></i> Notifications {NBADGE}</a></li>';
|
|
||||||
self::$loaded = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,61 +0,0 @@
|
|||||||
<div class="context-main-bg context-main p-3">
|
|
||||||
<legend class="text-center">Send Notification</legend>
|
|
||||||
<hr>
|
|
||||||
{ADMIN_BREADCRUMBS}
|
|
||||||
<form method="post" enctype="multipart/form-data">
|
|
||||||
<fieldset>
|
|
||||||
<!-- Group -->
|
|
||||||
<div class="mb-3 row">
|
|
||||||
<label for="groupSelect" class="col-lg-3 col-form-label text-end">Group:</label>
|
|
||||||
<div class="col-lg-6">
|
|
||||||
{groupSelect}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Recipients -->
|
|
||||||
<div class="mb-3 row">
|
|
||||||
<label for="expiration" class="col-lg-3 col-form-label text-end">Expiration:</label>
|
|
||||||
<div class="col-lg-6">
|
|
||||||
<select class="form-control" name="expiration" id="expiration">
|
|
||||||
<option value="0">forever</option>
|
|
||||||
<option value="1800">30 Minutes</option>
|
|
||||||
<option value="3600">60 Minutes</option>
|
|
||||||
<option value="14400">4 hours</option>
|
|
||||||
<option value="28800">8 hours</option>
|
|
||||||
<option value="43200">12 hours</option>
|
|
||||||
<option value="86400">24 hours</option>
|
|
||||||
<option value="172800">2 days</option>
|
|
||||||
<option value="259200">3 days</option>
|
|
||||||
<option value="432000">5 days</option>
|
|
||||||
<option value="604800">7 days</option>
|
|
||||||
<option value="1209600">2 weeks</option>
|
|
||||||
<option value="1814400">3 weeks</option>
|
|
||||||
<option value="2419200">4 weeks</option>
|
|
||||||
<option value="2592000">1 month</option>
|
|
||||||
<option value="5184000">2 months</option>
|
|
||||||
<option value="7776000">3 months</option>
|
|
||||||
<option value="15552000">6 months</option>
|
|
||||||
<option value="31536000">12 months</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Notification -->
|
|
||||||
<div class="mb-3 row">
|
|
||||||
<label for="notification" class="col-lg-3 col-form-label text-end">Notification:</label>
|
|
||||||
<div class="col-lg-6">
|
|
||||||
<textarea class="form-control" name="notification" id="notification" rows="6" maxlength="2000" required></textarea>
|
|
||||||
<small class="form-text text-muted">Max: 2000 characters</small>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Hidden Token -->
|
|
||||||
<input type="hidden" name="token" value="{TOKEN}">
|
|
||||||
</fieldset>
|
|
||||||
|
|
||||||
<!-- Submit Button -->
|
|
||||||
<div class="text-center">
|
|
||||||
<button type="submit" name="submit" value="submit" class="btn btn-primary btn-lg center-block">Send</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
@ -1 +0,0 @@
|
|||||||
<span class="badge bg-danger rounded-pill">{notificationCount}</span>
|
|
@ -1,41 +0,0 @@
|
|||||||
|
|
||||||
<div class="m-2 m-lg-4">
|
|
||||||
<div class="col-12 mx-5 col-sm-10 col-lg-8 mx-auto p-4 rounded shadow-sm context-main-bg">
|
|
||||||
<div class="row justify-content-center">
|
|
||||||
<div class="col-md-8">
|
|
||||||
<legend class="text-center">Notifications</legend>
|
|
||||||
<table class="table">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th style="width: 90%"></th>
|
|
||||||
<th style="width: 5%"></th>
|
|
||||||
<th style="width: 5%"></th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{LOOP}
|
|
||||||
<tr {unseenBadge}>
|
|
||||||
<td class=" context-main">{notification}</td>
|
|
||||||
<td>
|
|
||||||
{markReadLink}
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<a href="{ROOT_URL}notifications/delete/{ID}" class="btn btn-sm btn-danger">
|
|
||||||
<i class="fa fa-fw fa-trash"></i>
|
|
||||||
</a>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
{/LOOP}
|
|
||||||
{ALT}
|
|
||||||
<tr>
|
|
||||||
<td colspan="3" class="text-center context-main">
|
|
||||||
No Notifications
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
{/ALT}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
@ -1,41 +0,0 @@
|
|||||||
<!-- Notifications Dropdown -->
|
|
||||||
<div class="dropdown nav-item mx-2 my-2 my-lg-0">
|
|
||||||
<a
|
|
||||||
href="#"
|
|
||||||
class="d-flex align-items-center text-white text-decoration-none dropdown-toggle"
|
|
||||||
id="notificationsDropdown"
|
|
||||||
data-bs-toggle="dropdown"
|
|
||||||
aria-haspopup="true"
|
|
||||||
aria-expanded="false">
|
|
||||||
<i class="fa fa-fw fa-bell"></i><span class="">{NBADGE}</span>
|
|
||||||
</a>
|
|
||||||
<ul class="dropdown-menu dropdown-menu-dark dropdown-menu-end text-small shadow" aria-labelledby="notificationsDropdown">
|
|
||||||
{LOOP}
|
|
||||||
<!-- Notification Item -->
|
|
||||||
<li>
|
|
||||||
<a href="{ROOT_URL}notifications" class="dropdown-item">
|
|
||||||
<div class="media">
|
|
||||||
<div class="media-body">
|
|
||||||
<p class="small text-muted mb-1"><i class="fa fa-clock-o me-1"></i> {DTC}{createdAt}{/DTC}</p>
|
|
||||||
<span>{notification}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
{/LOOP}
|
|
||||||
{ALT}
|
|
||||||
<li class="px-3 text-center">
|
|
||||||
<strong>No Notifications</strong>
|
|
||||||
</li>
|
|
||||||
{/ALT}
|
|
||||||
<!-- Footer -->
|
|
||||||
<li>
|
|
||||||
<hr class="dropdown-divider">
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a href="/notifications" class="dropdown-item text-center">
|
|
||||||
See All Notifications
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
@ -1 +0,0 @@
|
|||||||
class="bg-info"
|
|
101
app/plugins/portfolio/controllers/admin/portfolio.php
Normal file
101
app/plugins/portfolio/controllers/admin/portfolio.php
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* app/plugins/portfolio/controllers/admin/portfolio.php
|
||||||
|
*
|
||||||
|
* This is the Portfolio admin controller.
|
||||||
|
*
|
||||||
|
* @package TP Portfolio
|
||||||
|
* @version 3.0
|
||||||
|
* @author Joey Kimsey <Joey@thetempusproject.com>
|
||||||
|
* @link https://TheTempusProject.com
|
||||||
|
* @license https://opensource.org/licenses/MIT [MIT LICENSE]
|
||||||
|
*/
|
||||||
|
namespace TheTempusProject\Controllers\Admin;
|
||||||
|
|
||||||
|
use TheTempusProject\Bedrock\Functions\Check;
|
||||||
|
use TheTempusProject\Bedrock\Functions\Input;
|
||||||
|
use TheTempusProject\Houdini\Classes\Issues;
|
||||||
|
use TheTempusProject\Houdini\Classes\Views;
|
||||||
|
use TheTempusProject\Houdini\Classes\Navigation;
|
||||||
|
use TheTempusProject\Houdini\Classes\Components;
|
||||||
|
use TheTempusProject\Classes\AdminController;
|
||||||
|
use TheTempusProject\Classes\Forms;
|
||||||
|
use TheTempusProject\Models\Links;
|
||||||
|
|
||||||
|
class Portfolio extends AdminController {
|
||||||
|
public static $links;
|
||||||
|
|
||||||
|
public function __construct() {
|
||||||
|
parent::__construct();
|
||||||
|
self::$links = new Links;
|
||||||
|
self::$title = 'Admin - Portfolio';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function index( $data = null ) {
|
||||||
|
Views::view( 'portfolio.admin.list', self::$links->listPaginated() );
|
||||||
|
}
|
||||||
|
|
||||||
|
public function create( $data = null ) {
|
||||||
|
if ( !Input::exists( 'submit' ) ) {
|
||||||
|
return Views::view( 'portfolio.admin.create' );
|
||||||
|
}
|
||||||
|
if ( !Forms::check( 'newLink' ) ) {
|
||||||
|
Issues::add( 'error', [ 'There was an error with your request.' => Check::userErrors() ] );
|
||||||
|
return $this->index();
|
||||||
|
}
|
||||||
|
$result = self::$links->create( Input::post( 'section' ), Input::post( 'title' ), Input::post( 'image' ), Input::post( 'url' ), Input::post( 'description' ) );
|
||||||
|
if ( $result ) {
|
||||||
|
Issues::add( 'success', 'Your link has been created.' );
|
||||||
|
return $this->index();
|
||||||
|
} else {
|
||||||
|
Issues::add( 'error', [ 'There was an unknown error submitting your data.' => Check::userErrors() ] );
|
||||||
|
return $this->index();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function edit( $data = null ) {
|
||||||
|
if ( !Input::exists( 'submit' ) ) {
|
||||||
|
return Views::view( 'portfolio.admin.edit', self::$links->findById( $data ) );
|
||||||
|
}
|
||||||
|
if ( !Forms::check( 'editLink' ) ) {
|
||||||
|
Issues::add( 'error', [ 'There was an error with your form.' => Check::userErrors() ] );
|
||||||
|
return $this->index();
|
||||||
|
}
|
||||||
|
$fields = [
|
||||||
|
'section' => Input::post( 'section' ),
|
||||||
|
'title' => Input::post( 'title' ),
|
||||||
|
'image' => Input::post( 'image' ),
|
||||||
|
'url' => Input::post( 'url' ),
|
||||||
|
'description' => Input::post( 'description' ),
|
||||||
|
];
|
||||||
|
if ( self::$links->update( $data, $fields ) ) {
|
||||||
|
Issues::add( 'success', 'Link Updated.' );
|
||||||
|
return $this->index();
|
||||||
|
}
|
||||||
|
Issues::add( 'error', 'There was an error with your request.' );
|
||||||
|
$this->index();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function view( $data = null ) {
|
||||||
|
$linkData = self::$links->findById( $data );
|
||||||
|
if ( $linkData !== false ) {
|
||||||
|
return Views::view( 'portfolio.admin.view', $linkData );
|
||||||
|
}
|
||||||
|
Issues::add( 'error', 'Link not found.' );
|
||||||
|
$this->index();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function delete( $data = null ) {
|
||||||
|
if ( $data == null ) {
|
||||||
|
if ( Input::exists( 'P_' ) ) {
|
||||||
|
$data = Input::post( 'P_' );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ( !self::$links->delete( (array) $data ) ) {
|
||||||
|
Issues::add( 'error', 'There was an error with your request.' );
|
||||||
|
} else {
|
||||||
|
Issues::add( 'success', 'Link has been deleted' );
|
||||||
|
}
|
||||||
|
$this->index();
|
||||||
|
}
|
||||||
|
}
|
36
app/plugins/portfolio/controllers/portfolio.php
Normal file
36
app/plugins/portfolio/controllers/portfolio.php
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* app/plugins/portfolio/controllers/portfolio.php
|
||||||
|
*
|
||||||
|
* This is the bug reports controller.
|
||||||
|
*
|
||||||
|
* @package TP Portfolio
|
||||||
|
* @version 3.0
|
||||||
|
* @author Joey Kimsey <Joey@thetempusproject.com>
|
||||||
|
* @link https://TheTempusProject.com
|
||||||
|
* @license https://opensource.org/licenses/MIT [MIT LICENSE]
|
||||||
|
*/
|
||||||
|
namespace TheTempusProject\Controllers;
|
||||||
|
|
||||||
|
use TheTempusProject\Houdini\Classes\Issues;
|
||||||
|
use TheTempusProject\Houdini\Classes\Views;
|
||||||
|
use TheTempusProject\Classes\Controller;
|
||||||
|
use TheTempusProject\Models\Links;
|
||||||
|
|
||||||
|
class Portfolio extends Controller {
|
||||||
|
protected static $links;
|
||||||
|
|
||||||
|
public function index() {
|
||||||
|
self::$links = new Links;
|
||||||
|
self::$title = '{SITENAME} - Portfolio';
|
||||||
|
self::$pageDescription = 'Its not the longest portfolio in the world, but I\'m certainly proud of it.';
|
||||||
|
|
||||||
|
$links = self::$links->listPaginated();
|
||||||
|
if ( false == $links ) {
|
||||||
|
Issues::add( 'error', 'Well, this is embarrassing, surely he wouldn\'t just have no portfolio..... right.... Dave? ... erm Joey?' );
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
Views::view( 'portfolio.portfolio', self::$links->listPaginated() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
79
app/plugins/portfolio/forms.php
Normal file
79
app/plugins/portfolio/forms.php
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* app/plugins/portfolio/forms.php
|
||||||
|
*
|
||||||
|
* This houses all of the form checking functions for this plugin.
|
||||||
|
*
|
||||||
|
* @package TP Portfolio
|
||||||
|
* @version 3.0
|
||||||
|
* @author Joey Kimsey <Joey@thetempusproject.com>
|
||||||
|
* @link https://TheTempusProject.com
|
||||||
|
* @license https://opensource.org/licenses/MIT [MIT LICENSE]
|
||||||
|
*/
|
||||||
|
namespace TheTempusProject\Plugins\Portfolio;
|
||||||
|
|
||||||
|
use TheTempusProject\Bedrock\Functions\Input;
|
||||||
|
use TheTempusProject\Bedrock\Functions\Check;
|
||||||
|
use TheTempusProject\Classes\Forms;
|
||||||
|
|
||||||
|
class PortfolioForms extends Forms {
|
||||||
|
/**
|
||||||
|
* Adds these functions to the form list.
|
||||||
|
*/
|
||||||
|
public function __construct() {
|
||||||
|
self::addHandler( 'newLink', __CLASS__, 'newLink' );
|
||||||
|
self::addHandler( 'editLink', __CLASS__, 'editLink' );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validates the new position post form.
|
||||||
|
*
|
||||||
|
* @return {bool}
|
||||||
|
*/
|
||||||
|
public static function newLink() {
|
||||||
|
if ( !Input::exists( 'section' ) ) {
|
||||||
|
self::addUserError( 'You must specify section' );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if ( !Input::exists( 'title' ) ) {
|
||||||
|
self::addUserError( 'You must specify title' );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if ( !Input::exists( 'url' ) ) {
|
||||||
|
self::addUserError( 'You must specify url' );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if ( !Input::exists( 'description' ) ) {
|
||||||
|
self::addUserError( 'You must specify description' );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validates the edit position post form.
|
||||||
|
*
|
||||||
|
* @return {bool}
|
||||||
|
*/
|
||||||
|
public static function editLink() {
|
||||||
|
if ( !Input::exists( 'section' ) ) {
|
||||||
|
self::addUserError( 'You must specify section' );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if ( !Input::exists( 'title' ) ) {
|
||||||
|
self::addUserError( 'You must specify title' );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if ( !Input::exists( 'url' ) ) {
|
||||||
|
self::addUserError( 'You must specify url' );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if ( !Input::exists( 'description' ) ) {
|
||||||
|
self::addUserError( 'You must specify description' );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
new PortfolioForms;
|
71
app/plugins/portfolio/models/links.php
Normal file
71
app/plugins/portfolio/models/links.php
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* app/plugins/portfolio/models/links.php
|
||||||
|
*
|
||||||
|
* This class is used for the manipulation of the links database table.
|
||||||
|
*
|
||||||
|
* @package TP Portfolio
|
||||||
|
* @version 3.0
|
||||||
|
* @author Joey Kimsey <Joey@thetempusproject.com>
|
||||||
|
* @link https://TheTempusProject.com
|
||||||
|
* @license https://opensource.org/licenses/MIT [MIT LICENSE]
|
||||||
|
*/
|
||||||
|
namespace TheTempusProject\Models;
|
||||||
|
|
||||||
|
use TheTempusProject\Bedrock\Classes\Config;
|
||||||
|
use TheTempusProject\Bedrock\Functions\Check;
|
||||||
|
use TheTempusProject\Canary\Bin\Canary as Debug;
|
||||||
|
use TheTempusProject\Classes\DatabaseModel;
|
||||||
|
use TheTempusProject\Plugins\Portfolio as Plugin;
|
||||||
|
|
||||||
|
class Links extends DatabaseModel {
|
||||||
|
public $tableName = 'portfolio_links';
|
||||||
|
public $databaseMatrix = [
|
||||||
|
[ 'section', 'varchar', '32' ],
|
||||||
|
[ 'title', 'varchar', '128' ],
|
||||||
|
[ 'image', 'varchar', '256' ],
|
||||||
|
[ 'url', 'varchar', '256' ],
|
||||||
|
[ 'description', 'text', '' ],
|
||||||
|
];
|
||||||
|
public $plugin;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The model constructor.
|
||||||
|
*/
|
||||||
|
public function __construct() {
|
||||||
|
parent::__construct();
|
||||||
|
$this->plugin = new Plugin;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function create( $section, $title, $image, $url, $description ) {
|
||||||
|
if ( !$this->plugin->checkEnabled() ) {
|
||||||
|
Debug::info( 'Portfolio is disabled in the config.' );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
$fields = [
|
||||||
|
'section' => $section,
|
||||||
|
'title' => $title,
|
||||||
|
'image' => $image,
|
||||||
|
'url' => $url,
|
||||||
|
'description' => $description,
|
||||||
|
];
|
||||||
|
if ( !self::$db->insert( $this->tableName, $fields ) ) {
|
||||||
|
Debug::info( 'Links::create - failed to insert to db' );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return self::$db->lastId();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function update( $id, $fields ) {
|
||||||
|
if ( !Check::id( $id ) ) {
|
||||||
|
Debug::info( 'modelBlog: illegal ID.' );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if ( !self::$db->update( $this->tableName, $id, $fields ) ) {
|
||||||
|
// new CustomException( 'linkUpdate' );
|
||||||
|
Debug::error( "Links:update: $id not updated: $fields" );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
44
app/plugins/portfolio/plugin.php
Normal file
44
app/plugins/portfolio/plugin.php
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* app/plugins/portfolio/plugin.php
|
||||||
|
*
|
||||||
|
* This houses all of the main plugin info and functionality.
|
||||||
|
*
|
||||||
|
* @package TP Portfolio
|
||||||
|
* @version 3.0
|
||||||
|
* @author Joey Kimsey <Joey@thetempusproject.com>
|
||||||
|
* @link https://TheTempusProject.com
|
||||||
|
* @license https://opensource.org/licenses/MIT [MIT LICENSE]
|
||||||
|
*/
|
||||||
|
namespace TheTempusProject\Plugins;
|
||||||
|
|
||||||
|
use TheTempusProject\Classes\Plugin;
|
||||||
|
|
||||||
|
class Portfolio extends Plugin {
|
||||||
|
public $pluginName = 'TP Portfolio';
|
||||||
|
public $pluginAuthor = 'JoeyK';
|
||||||
|
public $pluginWebsite = 'https://TheTempusProject.com';
|
||||||
|
public $modelVersion = '1.0';
|
||||||
|
public $pluginVersion = '3.0';
|
||||||
|
public $pluginDescription = 'A simple plugin which adds management for a portfolio.';
|
||||||
|
public $configName = 'portfolio';
|
||||||
|
public $configMatrix = [
|
||||||
|
'enabled' => [
|
||||||
|
'type' => 'radio',
|
||||||
|
'pretty' => 'Enable the portfolio Feature.',
|
||||||
|
'default' => true,
|
||||||
|
],
|
||||||
|
];
|
||||||
|
public $main_links = [
|
||||||
|
[
|
||||||
|
'text' => 'Portfolio',
|
||||||
|
'url' => '{ROOT_URL}portfolio/index',
|
||||||
|
],
|
||||||
|
];
|
||||||
|
public $admin_links = [
|
||||||
|
[
|
||||||
|
'text' => '<i class="fa-solid fa-star"></i> Portfolio',
|
||||||
|
'url' => '{ROOT_URL}admin/portfolio',
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
19
app/plugins/portfolio/views/admin/create.html
Normal file
19
app/plugins/portfolio/views/admin/create.html
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
<h2>Add Portfolio Link</h2>
|
||||||
|
<form action="#" method="POST">
|
||||||
|
<label for="section">Section Name:</label><br>
|
||||||
|
<input type="text" id="section" name="section" required><br><br>
|
||||||
|
|
||||||
|
<label for="title">Title:</label><br>
|
||||||
|
<input type="text" id="title" name="title" required><br><br>
|
||||||
|
|
||||||
|
<label for="image">Image:</label><br>
|
||||||
|
<input type="text" id="image" name="image" required><br><br>
|
||||||
|
|
||||||
|
<label for="url">URL:</label><br>
|
||||||
|
<input type="text" id="url" name="url" required><br><br>
|
||||||
|
|
||||||
|
<label for="description">Description:</label><br>
|
||||||
|
<textarea id="description" name="description" rows="20" cols="50" required></textarea><br><br>
|
||||||
|
|
||||||
|
<input type="submit" name="submit" value="Submit">
|
||||||
|
</form>
|
19
app/plugins/portfolio/views/admin/edit.html
Normal file
19
app/plugins/portfolio/views/admin/edit.html
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
<h2>Edit Portfolio Link</h2>
|
||||||
|
<form action="#" method="POST">
|
||||||
|
<label for="section">Section Name:</label><br>
|
||||||
|
<input type="text" id="section" name="section" value="{section}" required><br><br>
|
||||||
|
|
||||||
|
<label for="title">Title:</label><br>
|
||||||
|
<input type="text" id="title" name="title" value="{title}" required><br><br>
|
||||||
|
|
||||||
|
<label for="image">Image:</label><br>
|
||||||
|
<input type="text" id="image" name="image" value="{image}" required><br><br>
|
||||||
|
|
||||||
|
<label for="url">URL:</label><br>
|
||||||
|
<input type="text" id="url" name="url" value="{url}" required><br><br>
|
||||||
|
|
||||||
|
<label for="description">Description</label><br>
|
||||||
|
<textarea id="description" name="description" rows="20" cols="50" required>{description}</textarea><br><br>
|
||||||
|
|
||||||
|
<input type="submit" name="submit" value="Submit">
|
||||||
|
</form>
|
44
app/plugins/portfolio/views/admin/list.html
Normal file
44
app/plugins/portfolio/views/admin/list.html
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
<div class="context-main-bg context-main p-3">
|
||||||
|
<legend class="text-center">Portfolio Links</legend>
|
||||||
|
<hr>
|
||||||
|
{ADMIN_BREADCRUMBS}
|
||||||
|
<form action="{ROOT_URL}admin/portfolio/delete" method="post">
|
||||||
|
<table class="table table-striped">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th style="width: 30%">title</th>
|
||||||
|
<th style="width: 30%">section</th>
|
||||||
|
<th style="width: 25%">url</th>
|
||||||
|
<th style="width: 5%"></th>
|
||||||
|
<th style="width: 5%"></th>
|
||||||
|
<th style="width: 5%">
|
||||||
|
<INPUT type="checkbox" onchange="checkAll(this)" name="check.b" value="P_[]"/>
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{LOOP}
|
||||||
|
<tr>
|
||||||
|
<td><a href="{ROOT_URL}admin/portfolio/view/{ID}" class="text-decoration-none">{title}</a></td>
|
||||||
|
<td>{section}</td>
|
||||||
|
<td>{url}</td>
|
||||||
|
<td><a href="{ROOT_URL}admin/portfolio/edit/{ID}" class="btn btn-sm btn-warning" role="button"><i class="fa fa-fw fa-pencil"></i></a></td>
|
||||||
|
<td><a href="{ROOT_URL}admin/portfolio/delete/{ID}" class="btn btn-sm btn-danger" role="button"><i class="fa fa-fw fa-trash"></i></a></td>
|
||||||
|
<td>
|
||||||
|
<input type="checkbox" value="{ID}" name="P_[]">
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{/LOOP}
|
||||||
|
{ALT}
|
||||||
|
<tr>
|
||||||
|
<td colspan="7">
|
||||||
|
No results to show.
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{/ALT}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<a href="{ROOT_URL}admin/portfolio/create" class="btn btn-sm btn-primary" role="button">Create</a>
|
||||||
|
<button name="submit" value="submit" type="submit" class="btn btn-sm btn-danger"><i class="fa fa-fw fa-trash"></i></button>
|
||||||
|
</form>
|
||||||
|
</div>
|
16
app/plugins/portfolio/views/admin/view.html
Normal file
16
app/plugins/portfolio/views/admin/view.html
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
|
||||||
|
<legend>Portfolio Link</legend>
|
||||||
|
<hr>
|
||||||
|
<div class="media portfolio-link">
|
||||||
|
<div class="media-left">
|
||||||
|
<a href="#">
|
||||||
|
<img class="media-object" src="{image}" alt="{title} preview">
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="media-body">
|
||||||
|
<h4 class="media-heading"><a href="{url}">{title}</a></h4>
|
||||||
|
{description}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
30
app/plugins/portfolio/views/portfolio.html
Normal file
30
app/plugins/portfolio/views/portfolio.html
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
<div class="col-12 col-md-10 col-lg-8 offset-lg-2 offset-md-1 offset-0 mb-3 mb-lg-5 mt-2 mt-lg-4">
|
||||||
|
<div class="p-2 p-lg-4 mb-lg-4 m-2 rounded-3 context-main context-main-bg">
|
||||||
|
<h2 class="text-center">Portfolio</h2>
|
||||||
|
<hr>
|
||||||
|
{LOOP}
|
||||||
|
<div class="card context-main context-third-bg py-2 my-2">
|
||||||
|
<div class="row g-0 p-lg-2">
|
||||||
|
<div class="col-md-4 ps-2 d-flex justify-content-center align-items-center">
|
||||||
|
<a href="{url}">
|
||||||
|
<img class="img-fluid rounded" src="{image}" alt="{title} preview">
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-8">
|
||||||
|
<div class="card-body">
|
||||||
|
<h4 class="card-title">{title}</h4>
|
||||||
|
<p class="card-text">
|
||||||
|
{description}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/LOOP}
|
||||||
|
{ALT}
|
||||||
|
<div class="card context-main context-main-bg">
|
||||||
|
<p>None Found</p>
|
||||||
|
</div>
|
||||||
|
{/ALT}
|
||||||
|
</div>
|
||||||
|
</div>
|
102
app/plugins/resume/controllers/admin/resume.php
Normal file
102
app/plugins/resume/controllers/admin/resume.php
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* app/plugins/resume/controllers/admin/resume.php
|
||||||
|
*
|
||||||
|
* This is the Resume admin controller.
|
||||||
|
*
|
||||||
|
* @package TP Resume
|
||||||
|
* @version 3.0
|
||||||
|
* @author Joey Kimsey <Joey@thetempusproject.com>
|
||||||
|
* @link https://TheTempusProject.com
|
||||||
|
* @license https://opensource.org/licenses/MIT [MIT LICENSE]
|
||||||
|
*/
|
||||||
|
namespace TheTempusProject\Controllers\Admin;
|
||||||
|
|
||||||
|
use TheTempusProject\Bedrock\Functions\Check;
|
||||||
|
use TheTempusProject\Bedrock\Functions\Input;
|
||||||
|
use TheTempusProject\Houdini\Classes\Issues;
|
||||||
|
use TheTempusProject\Houdini\Classes\Views;
|
||||||
|
use TheTempusProject\Houdini\Classes\Navigation;
|
||||||
|
use TheTempusProject\Houdini\Classes\Components;
|
||||||
|
use TheTempusProject\Classes\AdminController;
|
||||||
|
use TheTempusProject\Classes\Forms;
|
||||||
|
use TheTempusProject\Models\Positions;
|
||||||
|
|
||||||
|
class Resume extends AdminController {
|
||||||
|
public static $positions;
|
||||||
|
|
||||||
|
public function __construct() {
|
||||||
|
parent::__construct();
|
||||||
|
self::$positions = new Positions;
|
||||||
|
self::$title = 'Admin - Resume';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function index( $data = null ) {
|
||||||
|
Views::view( 'resume.admin.list', self::$positions->listPaginated() );
|
||||||
|
}
|
||||||
|
|
||||||
|
public function create( $data = null ) {
|
||||||
|
if ( !Input::exists( 'submit' ) ) {
|
||||||
|
return Views::view( 'resume.admin.create' );
|
||||||
|
}
|
||||||
|
if ( !Forms::check( 'newPosition' ) ) {
|
||||||
|
Issues::add( 'error', [ 'There was an error with your request.' => Check::userErrors() ] );
|
||||||
|
return $this->index();
|
||||||
|
}
|
||||||
|
$result = self::$positions->create( Input::post( 'name' ), Input::post( 'position' ), Input::post( 'start' ), Input::post( 'end' ), Input::post( 'details' ), Input::post( 'detailsNobs' ) );
|
||||||
|
if ( $result ) {
|
||||||
|
Issues::add( 'success', 'Your position has been created.' );
|
||||||
|
return $this->index();
|
||||||
|
} else {
|
||||||
|
Issues::add( 'error', [ 'There was an unknown error submitting your data.' => Check::userErrors() ] );
|
||||||
|
return $this->index();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function edit( $data = null ) {
|
||||||
|
if ( !Input::exists( 'submit' ) ) {
|
||||||
|
return Views::view( 'resume.admin.edit', self::$positions->findById( $data ) );
|
||||||
|
}
|
||||||
|
if ( !Forms::check( 'editPosition' ) ) {
|
||||||
|
Issues::add( 'error', [ 'There was an error with your form.' => Check::userErrors() ] );
|
||||||
|
return $this->index();
|
||||||
|
}
|
||||||
|
$fields = [
|
||||||
|
'name' => Input::post( 'name' ),
|
||||||
|
'position' => Input::post( 'position' ),
|
||||||
|
'start' => Input::post( 'start' ),
|
||||||
|
'end' => Input::post( 'end' ),
|
||||||
|
'details' => Input::post( 'details' ),
|
||||||
|
'details_nobs' => Input::post( 'detailsNobs' ),
|
||||||
|
];
|
||||||
|
if ( self::$positions->update( $data, $fields ) ) {
|
||||||
|
Issues::add( 'success', 'Position Updated.' );
|
||||||
|
return $this->index();
|
||||||
|
}
|
||||||
|
Issues::add( 'error', 'There was an error with your request.' );
|
||||||
|
$this->index();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function view( $data = null ) {
|
||||||
|
$positionData = self::$positions->findById( $data );
|
||||||
|
if ( $positionData !== false ) {
|
||||||
|
return Views::view( 'resume.admin.view', $positionData );
|
||||||
|
}
|
||||||
|
Issues::add( 'error', 'Position not found.' );
|
||||||
|
$this->index();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function delete( $data = null ) {
|
||||||
|
if ( $data == null ) {
|
||||||
|
if ( Input::exists( 'P_' ) ) {
|
||||||
|
$data = Input::post( 'P_' );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ( !self::$positions->delete( (array) $data ) ) {
|
||||||
|
Issues::add( 'error', 'There was an error with your request.' );
|
||||||
|
} else {
|
||||||
|
Issues::add( 'success', 'Position has been deleted' );
|
||||||
|
}
|
||||||
|
$this->index();
|
||||||
|
}
|
||||||
|
}
|
67
app/plugins/resume/controllers/resume.php
Normal file
67
app/plugins/resume/controllers/resume.php
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* app/plugins/resume/controllers/resume.php
|
||||||
|
*
|
||||||
|
* This is the bug reports controller.
|
||||||
|
*
|
||||||
|
* @package TP Resume
|
||||||
|
* @version 3.0
|
||||||
|
* @author Joey Kimsey <Joey@thetempusproject.com>
|
||||||
|
* @link https://TheTempusProject.com
|
||||||
|
* @license https://opensource.org/licenses/MIT [MIT LICENSE]
|
||||||
|
*/
|
||||||
|
namespace TheTempusProject\Controllers;
|
||||||
|
|
||||||
|
use TheTempusProject\Houdini\Classes\Issues;
|
||||||
|
use TheTempusProject\Houdini\Classes\Views;
|
||||||
|
use TheTempusProject\Classes\Controller;
|
||||||
|
use TheTempusProject\Models\Positions;
|
||||||
|
use TheTempusProject\Bedrock\Functions\Input;
|
||||||
|
use TheTempusProject\Houdini\Classes\Components;
|
||||||
|
use TheTempusProject\Houdini\Classes\Template;
|
||||||
|
|
||||||
|
class Resume extends Controller {
|
||||||
|
protected static $positions;
|
||||||
|
|
||||||
|
|
||||||
|
public function __construct() {
|
||||||
|
parent::__construct();
|
||||||
|
Components::append( 'TEMPLATE_JS_INCLUDES', Template::parse(
|
||||||
|
'<script language="JavaScript" crossorigin="anonymous" type="text/javascript" src="{ROOT_URL}app/plugins/resume/js/resume.js"></script>'
|
||||||
|
) );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function index() {
|
||||||
|
self::$positions = new Positions;
|
||||||
|
self::$title = '{SITENAME} - Resume';
|
||||||
|
self::$pageDescription = 'Its not the longest resume in the world, but I\'m certainly proud of it.';
|
||||||
|
|
||||||
|
$positions = self::$positions->listPaginated();
|
||||||
|
if ( false == $positions ) {
|
||||||
|
Issues::add( 'error', 'Well, this is embarrassing, surely he wouldn\'t just have no resume..... right.... Dave? ... erm Joey?' );
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
Components::set( 'RESUME_NAV', Views::simpleView( 'resume.nav') );
|
||||||
|
Components::set( 'RESUME_DOWNLOADS', Views::simpleView( 'resume.download') );
|
||||||
|
if ( !Input::exists( 'view' ) ) {
|
||||||
|
return Views::view( 'resume.resume', $positions );
|
||||||
|
} else {
|
||||||
|
Components::append( 'TEMPLATE_CSS_INCLUDES', Template::parse('<link rel="stylesheet" href="{ROOT_URL}app/plugins/resume/css/timeline.css" />') );
|
||||||
|
$side = 'left';
|
||||||
|
foreach ($positions as $key => $position) {
|
||||||
|
$position->side = $side; // Add the new entry for side
|
||||||
|
$side = ($side === 'left') ? 'right' : 'left'; // Alternate between left and right
|
||||||
|
}
|
||||||
|
return Views::view( 'resume.timeline', $positions );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public function test() {
|
||||||
|
self::$positions = new Positions;
|
||||||
|
self::$title = '{SITENAME} - Resume';
|
||||||
|
self::$pageDescription = 'Its not the longest resume in the world, but I\'m certainly proud of it.';
|
||||||
|
|
||||||
|
return Views::view( 'resume.test' );
|
||||||
|
}
|
||||||
|
}
|
131
app/plugins/resume/css/timeline.css
Normal file
131
app/plugins/resume/css/timeline.css
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
* {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
body {
|
||||||
|
/* background-color: #474e5d;
|
||||||
|
font-family: Helvetica, sans-serif; */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The actual timeline (the vertical ruler) */
|
||||||
|
.resume-timeline {
|
||||||
|
position: relative;
|
||||||
|
max-width: 1200px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The actual timeline (the vertical ruler) */
|
||||||
|
.resume-timeline::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
width: 6px;
|
||||||
|
background-color: #B5B5B5;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
left: 50%;
|
||||||
|
margin-left: -3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Container around content */
|
||||||
|
.timeline-container {
|
||||||
|
padding: 10px 40px;
|
||||||
|
position: relative;
|
||||||
|
background-color: inherit;
|
||||||
|
width: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The circles on the timeline */
|
||||||
|
.timeline-container::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
width: 25px;
|
||||||
|
height: 25px;
|
||||||
|
right: -13px;
|
||||||
|
background-color: #337ab7;
|
||||||
|
border: 4px solid #FF9F55;
|
||||||
|
top: 15px;
|
||||||
|
border-radius: 50%;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Place the container to the left */
|
||||||
|
.left {
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Place the container to the right */
|
||||||
|
.right {
|
||||||
|
left: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Add arrows to the left container (pointing right) */
|
||||||
|
.left::before {
|
||||||
|
content: " ";
|
||||||
|
height: 0;
|
||||||
|
position: absolute;
|
||||||
|
top: 22px;
|
||||||
|
width: 0;
|
||||||
|
z-index: 1;
|
||||||
|
right: 30px;
|
||||||
|
border: medium solid white;
|
||||||
|
border-width: 10px 0 10px 10px;
|
||||||
|
border-color: transparent transparent transparent white;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Add arrows to the right container (pointing left) */
|
||||||
|
.right::before {
|
||||||
|
content: " ";
|
||||||
|
height: 0;
|
||||||
|
position: absolute;
|
||||||
|
top: 22px;
|
||||||
|
width: 0;
|
||||||
|
z-index: 1;
|
||||||
|
left: 30px;
|
||||||
|
border: medium solid white;
|
||||||
|
border-width: 10px 10px 10px 0;
|
||||||
|
border-color: transparent white transparent transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Fix the circle for containers on the right side */
|
||||||
|
.right::after {
|
||||||
|
left: -11px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The actual content */
|
||||||
|
.timeline-content {
|
||||||
|
padding: 20px 30px;
|
||||||
|
position: relative;
|
||||||
|
border-radius: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Media queries - Responsive timeline on screens less than 600px wide */
|
||||||
|
@media screen and (max-width: 600px) {
|
||||||
|
/* Place the timelime to the left */
|
||||||
|
.resume-timeline::after {
|
||||||
|
left: 31px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Full-width containers */
|
||||||
|
.timeline-container {
|
||||||
|
width: 100%;
|
||||||
|
padding-left: 70px;
|
||||||
|
padding-right: 25px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Make sure that all arrows are pointing leftwards */
|
||||||
|
.timeline-container::before {
|
||||||
|
left: 60px;
|
||||||
|
border: medium solid white;
|
||||||
|
border-width: 10px 10px 10px 0;
|
||||||
|
border-color: transparent white transparent transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Make sure all circles are at the same spot */
|
||||||
|
.left::after, .right::after {
|
||||||
|
left: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Make all right containers behave like the left ones */
|
||||||
|
.right {
|
||||||
|
left: 0%;
|
||||||
|
}
|
||||||
|
}
|
87
app/plugins/resume/forms.php
Normal file
87
app/plugins/resume/forms.php
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* app/plugins/resume/forms.php
|
||||||
|
*
|
||||||
|
* This houses all of the form checking functions for this plugin.
|
||||||
|
*
|
||||||
|
* @package TP Resume
|
||||||
|
* @version 3.0
|
||||||
|
* @author Joey Kimsey <Joey@thetempusproject.com>
|
||||||
|
* @link https://TheTempusProject.com
|
||||||
|
* @license https://opensource.org/licenses/MIT [MIT LICENSE]
|
||||||
|
*/
|
||||||
|
namespace TheTempusProject\Plugins\Resume;
|
||||||
|
|
||||||
|
use TheTempusProject\Bedrock\Functions\Input;
|
||||||
|
use TheTempusProject\Bedrock\Functions\Check;
|
||||||
|
use TheTempusProject\Classes\Forms;
|
||||||
|
|
||||||
|
class ResumeForms extends Forms {
|
||||||
|
/**
|
||||||
|
* Adds these functions to the form list.
|
||||||
|
*/
|
||||||
|
public function __construct() {
|
||||||
|
self::addHandler( 'newPosition', __CLASS__, 'newPosition' );
|
||||||
|
self::addHandler( 'editPosition', __CLASS__, 'editPosition' );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validates the new position post form.
|
||||||
|
*
|
||||||
|
* @return {bool}
|
||||||
|
*/
|
||||||
|
public static function newPosition() {
|
||||||
|
if ( !Input::exists( 'name' ) ) {
|
||||||
|
self::addUserError( 'You must specify name' );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if ( !Input::exists( 'position' ) ) {
|
||||||
|
self::addUserError( 'You must specify position' );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if ( !Input::exists( 'start' ) ) {
|
||||||
|
self::addUserError( 'You must specify start' );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if ( !Input::exists( 'end' ) ) {
|
||||||
|
self::addUserError( 'You must specify end' );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if ( !Input::exists( 'details' ) ) {
|
||||||
|
self::addUserError( 'You must specify details' );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validates the edit position post form.
|
||||||
|
*
|
||||||
|
* @return {bool}
|
||||||
|
*/
|
||||||
|
public static function editPosition() {
|
||||||
|
if ( !Input::exists( 'name' ) ) {
|
||||||
|
self::addUserError( 'You must specify name' );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if ( !Input::exists( 'position' ) ) {
|
||||||
|
self::addUserError( 'You must specify position' );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if ( !Input::exists( 'start' ) ) {
|
||||||
|
self::addUserError( 'You must specify start' );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if ( !Input::exists( 'end' ) ) {
|
||||||
|
self::addUserError( 'You must specify end' );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if ( !Input::exists( 'details' ) ) {
|
||||||
|
self::addUserError( 'You must specify details' );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
new ResumeForms;
|
14
app/plugins/resume/js/resume.js
Normal file
14
app/plugins/resume/js/resume.js
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
|
||||||
|
document.addEventListener('DOMContentLoaded', function () {
|
||||||
|
const hideBsCheckbox = document.getElementById('hidebs');
|
||||||
|
|
||||||
|
if (hideBsCheckbox) {
|
||||||
|
hideBsCheckbox.addEventListener('change', function () {
|
||||||
|
const detailsElements = document.querySelectorAll('.details, .details_nobs');
|
||||||
|
detailsElements.forEach(function (element) {
|
||||||
|
console.error(element);
|
||||||
|
element.classList.toggle('d-none');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
94
app/plugins/resume/models/positions.php
Normal file
94
app/plugins/resume/models/positions.php
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* app/plugins/resume/models/positions.php
|
||||||
|
*
|
||||||
|
* This class is used for the manipulation of the positions database table.
|
||||||
|
*
|
||||||
|
* @package TP Resume
|
||||||
|
* @version 3.0
|
||||||
|
* @author Joey Kimsey <Joey@thetempusproject.com>
|
||||||
|
* @link https://TheTempusProject.com
|
||||||
|
* @license https://opensource.org/licenses/MIT [MIT LICENSE]
|
||||||
|
*/
|
||||||
|
namespace TheTempusProject\Models;
|
||||||
|
|
||||||
|
use TheTempusProject\Bedrock\Classes\Config;
|
||||||
|
use TheTempusProject\Bedrock\Functions\Check;
|
||||||
|
use TheTempusProject\Canary\Bin\Canary as Debug;
|
||||||
|
use TheTempusProject\Classes\DatabaseModel;
|
||||||
|
use TheTempusProject\Plugins\Resume as Plugin;
|
||||||
|
|
||||||
|
class Positions extends DatabaseModel {
|
||||||
|
public $tableName = 'positions';
|
||||||
|
public $databaseMatrix = [
|
||||||
|
[ 'name', 'varchar', '128' ],
|
||||||
|
[ 'position', 'varchar', '128' ],
|
||||||
|
[ 'start', 'varchar', '16' ],
|
||||||
|
[ 'end', 'varchar', '16' ],
|
||||||
|
[ 'details', 'text', '' ],
|
||||||
|
[ 'details_nobs', 'text', '' ],
|
||||||
|
[ 'image', 'varchar', '256' ],
|
||||||
|
];
|
||||||
|
public $plugin;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The model constructor.
|
||||||
|
*/
|
||||||
|
public function __construct() {
|
||||||
|
parent::__construct();
|
||||||
|
$this->plugin = new Plugin;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function create( $name, $position, $start, $end, $details, $detailsNobs = '' ) {
|
||||||
|
if ( !$this->plugin->checkEnabled() ) {
|
||||||
|
Debug::info( 'Resume is disabled in the config.' );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if ( empty( $detailsNobs ) ) {
|
||||||
|
$detailsNobs = $details;
|
||||||
|
}
|
||||||
|
$fields = [
|
||||||
|
'name' => $name,
|
||||||
|
'position' => $position,
|
||||||
|
'start' => $start,
|
||||||
|
'end' => $end,
|
||||||
|
'details' => $details,
|
||||||
|
'details_nobs' => $detailsNobs,
|
||||||
|
];
|
||||||
|
if ( !self::$db->insert( $this->tableName, $fields ) ) {
|
||||||
|
Debug::info( 'Position::create - failed to insert to db' );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return self::$db->lastId();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function filter( $postArray, $params = [] ) {
|
||||||
|
foreach ( $postArray as $instance ) {
|
||||||
|
if ( !is_object( $instance ) ) {
|
||||||
|
$instance = $postArray;
|
||||||
|
$end = true;
|
||||||
|
}
|
||||||
|
$instance->prettyStart = $instance->start;
|
||||||
|
$instance->prettyEnd = $instance->end;
|
||||||
|
$out[] = $instance;
|
||||||
|
if ( !empty( $end ) ) {
|
||||||
|
$out = $out[0];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $out;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function update( $id, $fields ) {
|
||||||
|
if ( !Check::id( $id ) ) {
|
||||||
|
Debug::info( 'Positions:update: illegal ID.' );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if ( !self::$db->update( $this->tableName, $id, $fields ) ) {
|
||||||
|
// new CustomException( 'positionUpdate' );
|
||||||
|
Debug::error( "Positions:update: $id not updated: $fields" );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
44
app/plugins/resume/plugin.php
Normal file
44
app/plugins/resume/plugin.php
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* app/plugins/resume/plugin.php
|
||||||
|
*
|
||||||
|
* This houses all of the main plugin info and functionality.
|
||||||
|
*
|
||||||
|
* @package TP Resume
|
||||||
|
* @version 3.0
|
||||||
|
* @author Joey Kimsey <Joey@thetempusproject.com>
|
||||||
|
* @link https://TheTempusProject.com
|
||||||
|
* @license https://opensource.org/licenses/MIT [MIT LICENSE]
|
||||||
|
*/
|
||||||
|
namespace TheTempusProject\Plugins;
|
||||||
|
|
||||||
|
use TheTempusProject\Classes\Plugin;
|
||||||
|
|
||||||
|
class Resume extends Plugin {
|
||||||
|
public $pluginName = 'TP Resume';
|
||||||
|
public $pluginAuthor = 'JoeyK';
|
||||||
|
public $pluginWebsite = 'https://TheTempusProject.com';
|
||||||
|
public $modelVersion = '1.0';
|
||||||
|
public $pluginVersion = '3.0';
|
||||||
|
public $pluginDescription = 'A simple plugin which adds management for a resume.';
|
||||||
|
public $configName = 'resume';
|
||||||
|
public $configMatrix = [
|
||||||
|
'enabled' => [
|
||||||
|
'type' => 'radio',
|
||||||
|
'pretty' => 'Enable the resume Feature.',
|
||||||
|
'default' => true,
|
||||||
|
],
|
||||||
|
];
|
||||||
|
public $main_links = [
|
||||||
|
[
|
||||||
|
'text' => 'Resume',
|
||||||
|
'url' => '{ROOT_URL}resume/index',
|
||||||
|
],
|
||||||
|
];
|
||||||
|
public $admin_links = [
|
||||||
|
[
|
||||||
|
'text' => '<i class="fa-solid fa-paperclip"></i> Resume',
|
||||||
|
'url' => '{ROOT_URL}admin/resume',
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
22
app/plugins/resume/views/admin/create.html
Normal file
22
app/plugins/resume/views/admin/create.html
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
<h2>Resume Entry Form</h2>
|
||||||
|
<form action="#" method="POST">
|
||||||
|
<label for="name">Company Name:</label><br>
|
||||||
|
<input type="text" id="name" name="name" required><br><br>
|
||||||
|
|
||||||
|
<label for="position">Position:</label><br>
|
||||||
|
<input type="text" id="position" name="position" required><br><br>
|
||||||
|
|
||||||
|
<label for="start">Start Month/Year:</label><br>
|
||||||
|
<input type="month" id="start" name="start" required><br><br>
|
||||||
|
|
||||||
|
<label for="end">End Month/Year:</label><br>
|
||||||
|
<input type="month" id="end" name="end" required><br><br>
|
||||||
|
|
||||||
|
<label for="details">Details:</label><br>
|
||||||
|
<textarea id="details" name="details" rows="20" cols="50" required></textarea><br><br>
|
||||||
|
|
||||||
|
<label for="details">Details ( No BS ):</label><br>
|
||||||
|
<textarea id="detailsNobs" name="detailsNobs" rows="20" cols="50" required></textarea><br><br>
|
||||||
|
|
||||||
|
<input type="submit" name="submit" value="Submit">
|
||||||
|
</form>
|
22
app/plugins/resume/views/admin/edit.html
Normal file
22
app/plugins/resume/views/admin/edit.html
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
<h2>Edit Resume Position</h2>
|
||||||
|
<form action="#" method="POST">
|
||||||
|
<label for="name">Company Name:</label><br>
|
||||||
|
<input type="text" id="name" name="name" value="{name}" required><br><br>
|
||||||
|
|
||||||
|
<label for="position">Position:</label><br>
|
||||||
|
<input type="text" id="position" name="position" value="{position}" required><br><br>
|
||||||
|
|
||||||
|
<label for="start">Start Month/Year:</label><br>
|
||||||
|
<input type="month" id="start" name="start" value="{start}" required><br><br>
|
||||||
|
|
||||||
|
<label for="end">End Month/Year:</label><br>
|
||||||
|
<input type="month" id="end" name="end" value="{end}" required><br><br>
|
||||||
|
|
||||||
|
<label for="details">Details:</label><br>
|
||||||
|
<textarea id="details" name="details" rows="20" cols="50" required>{details}</textarea><br><br>
|
||||||
|
|
||||||
|
<label for="details">Details ( No BS ):</label><br>
|
||||||
|
<textarea id="detailsNobs" name="detailsNobs" rows="20" cols="50" required>{details_nobs}</textarea><br><br>
|
||||||
|
|
||||||
|
<input type="submit" name="submit" value="Submit">
|
||||||
|
</form>
|
46
app/plugins/resume/views/admin/list.html
Normal file
46
app/plugins/resume/views/admin/list.html
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
<div class="context-main-bg context-main p-3">
|
||||||
|
<legend class="text-center">Resume Positions</legend>
|
||||||
|
<hr>
|
||||||
|
{ADMIN_BREADCRUMBS}
|
||||||
|
<form action="{ROOT_URL}admin/resume/delete" method="post">
|
||||||
|
<table class="table table-striped">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th style="width: 30%">Name</th>
|
||||||
|
<th style="width: 20%">Position</th>
|
||||||
|
<th style="width: 10%">Start</th>
|
||||||
|
<th style="width: 10%">End</th>
|
||||||
|
<th style="width: 10%"></th>
|
||||||
|
<th style="width: 10%"></th>
|
||||||
|
<th style="width: 10%">
|
||||||
|
<INPUT type="checkbox" onchange="checkAll(this)" name="check.b" value="P_[]"/>
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{LOOP}
|
||||||
|
<tr>
|
||||||
|
<td><a href="{ROOT_URL}admin/resume/view/{ID}" class="text-decoration-none">{name}</a></td>
|
||||||
|
<td>{position}</td>
|
||||||
|
<td>{prettyStart}</td>
|
||||||
|
<td>{prettyEnd}</td>
|
||||||
|
<td><a href="{ROOT_URL}admin/resume/edit/{ID}" class="btn btn-sm btn-warning" role="button"><i class="fa fa-fw fa-pencil"></i></a></td>
|
||||||
|
<td><a href="{ROOT_URL}admin/resume/delete/{ID}" class="btn btn-sm btn-danger" role="button"><i class="fa fa-fw fa-trash"></i></a></td>
|
||||||
|
<td>
|
||||||
|
<input type="checkbox" value="{ID}" name="P_[]">
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{/LOOP}
|
||||||
|
{ALT}
|
||||||
|
<tr>
|
||||||
|
<td colspan="7">
|
||||||
|
No results to show.
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{/ALT}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<a href="{ROOT_URL}admin/resume/create" class="btn btn-sm btn-primary" role="button">Create</a>
|
||||||
|
<button name="submit" value="submit" type="submit" class="btn btn-sm btn-danger"><i class="fa fa-fw fa-trash"></i></button>
|
||||||
|
</form>
|
||||||
|
</div>
|
8
app/plugins/resume/views/admin/view.html
Normal file
8
app/plugins/resume/views/admin/view.html
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<legend>Resume Position</legend>
|
||||||
|
<div class="resume-position">
|
||||||
|
<h3 class="resume-position-title">{name}</h3>
|
||||||
|
<p><b>{position}</b> from <i>{prettyStart}</i> to <i>{prettyEnd}</i></p>
|
||||||
|
<div class="well">
|
||||||
|
{details}
|
||||||
|
</div>
|
||||||
|
</div>
|
13
app/plugins/resume/views/download.html
Normal file
13
app/plugins/resume/views/download.html
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<div class="my-3 row" role="group" aria-label="Resume Downloads">
|
||||||
|
<!-- Adjusted the col-3 div -->
|
||||||
|
<div class="col-4 col-md-3 d-flex align-items-center justify-content-end">
|
||||||
|
<h3>Download</h3>
|
||||||
|
</div>
|
||||||
|
<!-- Button group -->
|
||||||
|
<div class="btn-group btn-group-justified col-8 col-md-6" role="group" aria-label="Resume Downloads">
|
||||||
|
<a href="/downloads/resume/docx" class="btn btn-primary" role="button" target="_blank" download>Word</a>
|
||||||
|
<a href="/downloads/resume/pdf" class="btn btn-primary" role="button" target="_blank" download>PDF</a>
|
||||||
|
<a href="/downloads/resume/md" class="btn btn-primary" role="button" target="_blank" download>Markdown</a>
|
||||||
|
<a href="/downloads/resume/txt" class="btn btn-primary" role="button" target="_blank" download>Text</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
10
app/plugins/resume/views/nav.html
Normal file
10
app/plugins/resume/views/nav.html
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<div class="mb-3 row" role="group" aria-label="Resume View Type">
|
||||||
|
<div class="btn-group btn-group-justified col-6 offset-lg-3" role="group" aria-label="Resume View Type">
|
||||||
|
<a href="?view=tiimeline" class="btn btn-primary" role="button">Timeline</a>
|
||||||
|
<a href="?" class="btn btn-primary" role="button">Standard</a>
|
||||||
|
</div>
|
||||||
|
<div class="form-check form-switch col-6 col-lg-3 d-flex align-items-center justify-content-center justify-content-lg-start">
|
||||||
|
<input class="form-check-input" type="checkbox" role="switch" name="hidebs" id="hidebs" value="true">
|
||||||
|
<label class="form-check-label ps-2" for="hidebs">Hide the Fluff</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
36
app/plugins/resume/views/resume.html
Normal file
36
app/plugins/resume/views/resume.html
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
<div class="col-12 col-md-10 col-lg-8 offset-lg-2 offset-md-1 offset-0 mb-3 mb-lg-5 mt-2 mt-lg-4">
|
||||||
|
<div class="p-2 p-lg-4 mb-lg-4 m-2 rounded-3 context-main context-main-bg">
|
||||||
|
<h2 class="text-center">Resume</h2>
|
||||||
|
<hr>
|
||||||
|
{RESUME_NAV}
|
||||||
|
{LOOP}
|
||||||
|
<div class="card context-main context-third-bg py-2 my-2 rounded">
|
||||||
|
<div class="row g-0 px-3">
|
||||||
|
<div class="col-md-4 d-flex justify-content-center align-items-center p-2">
|
||||||
|
<img class="img-fluid" src="{image}" alt="{name}">
|
||||||
|
</div>
|
||||||
|
<div class="col-md-8">
|
||||||
|
<div class="card-body">
|
||||||
|
<h4 class="card-title">{name}</h4>
|
||||||
|
<p class="card-text">
|
||||||
|
<b>{position}</b> from <i>{prettyStart}</i> to <i>{prettyEnd}</i>
|
||||||
|
</p>
|
||||||
|
<div class="details card-text">
|
||||||
|
{details}
|
||||||
|
</div>
|
||||||
|
<div class="details_nobs card-text d-none">
|
||||||
|
{details_nobs}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/LOOP}
|
||||||
|
{ALT}
|
||||||
|
<div class="resume-position">
|
||||||
|
<p>None Found</p>
|
||||||
|
</div>
|
||||||
|
{/ALT}
|
||||||
|
{RESUME_DOWNLOADS}
|
||||||
|
</div>
|
||||||
|
</div>
|
35
app/plugins/resume/views/timeline.html
Normal file
35
app/plugins/resume/views/timeline.html
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
<div class="col-12 col-md-10 col-lg-8 offset-lg-2 offset-md-1 offset-0 mb-3 mb-lg-5 mt-2 mt-lg-4">
|
||||||
|
<div class="p-2 p-lg-4 mb-lg-4 m-2 rounded-3 context-main context-main-bg">
|
||||||
|
<h2 class="text-center">Resume</h2>
|
||||||
|
<hr>
|
||||||
|
{RESUME_NAV}
|
||||||
|
<div class="details_nobs card-text d-none text-center col-6 offset-3">
|
||||||
|
<p>
|
||||||
|
Honestly, I really wanted to do something fun and interactive here.
|
||||||
|
Unfortunately I didn't have a specific idea in mind and this was abandoned in favor of spending my time somewhere with a bigger impact.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
{LOOP}
|
||||||
|
<div class="resume-timeline">
|
||||||
|
<div class="timeline-container {side}">
|
||||||
|
<div class="timeline-content context-third-bg">
|
||||||
|
<h3 class="resume-position-title">{name}</h3>
|
||||||
|
<p><b>{position}</b> from <i>{prettyStart}</i> to <i>{prettyEnd}</i></p>
|
||||||
|
<div class="details card-text">
|
||||||
|
{details}
|
||||||
|
</div>
|
||||||
|
<div class="details_nobs card-text d-none">
|
||||||
|
{details_nobs}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/LOOP}
|
||||||
|
{ALT}
|
||||||
|
<div class="resume-timeline">
|
||||||
|
<p>None Found</p>
|
||||||
|
</div>
|
||||||
|
{/ALT}
|
||||||
|
{RESUME_DOWNLOADS}
|
||||||
|
</div>
|
||||||
|
</div>
|
@ -1,65 +0,0 @@
|
|||||||
<?php
|
|
||||||
/**
|
|
||||||
* app/plugins/subscribe/controllers/admin/subscriptions.php
|
|
||||||
*
|
|
||||||
* This is the subscriptions admin controller.
|
|
||||||
*
|
|
||||||
* @package TP Subscribe
|
|
||||||
* @version 5.0.1
|
|
||||||
* @author Joey Kimsey <Joey@thetempusproject.com>
|
|
||||||
* @link https://TheTempusProject.com
|
|
||||||
* @license https://opensource.org/licenses/MIT [MIT LICENSE]
|
|
||||||
*/
|
|
||||||
namespace TheTempusProject\Controllers\Admin;
|
|
||||||
|
|
||||||
use TheTempusProject\Bedrock\Functions\Input;
|
|
||||||
use TheTempusProject\Bedrock\Functions\Check;
|
|
||||||
use TheTempusProject\Houdini\Classes\Issues;
|
|
||||||
use TheTempusProject\Houdini\Classes\Views;
|
|
||||||
use TheTempusProject\Classes\AdminController;
|
|
||||||
use TheTempusProject\Models\Subscribe;
|
|
||||||
use TheTempusProject\Classes\Forms;
|
|
||||||
|
|
||||||
class Subscriptions extends AdminController {
|
|
||||||
public static $subscribe;
|
|
||||||
|
|
||||||
public function __construct() {
|
|
||||||
parent::__construct();
|
|
||||||
self::$title = 'Admin - Email Subscribers';
|
|
||||||
self::$subscribe = new Subscribe;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function delete( $data = null ) {
|
|
||||||
if ( $data == null ) {
|
|
||||||
if ( Input::exists( 'submit' ) ) {
|
|
||||||
$data = Input::post( 'S_' );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ( !self::$subscribe->delete( (array) $data ) ) {
|
|
||||||
Issues::add( 'error', 'There was an error with your request, please try again.' );
|
|
||||||
} else {
|
|
||||||
Issues::add( 'success', 'Subscriber removed.' );
|
|
||||||
}
|
|
||||||
$this->index();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function add( $data = null ) {
|
|
||||||
if ( !Input::exists( 'submit' ) ) {
|
|
||||||
return Views::view( 'subscribe.admin.add' );
|
|
||||||
}
|
|
||||||
if ( !Forms::check( 'subscribe' ) ) {
|
|
||||||
Issues::add( 'error', [ 'There was an error with your request.' => Check::userErrors() ] );
|
|
||||||
return $this->index();
|
|
||||||
}
|
|
||||||
if ( !self::$subscribe->add( Input::post( 'email' ) ) ) {
|
|
||||||
Issues::add( 'error', 'There was an error with your request, please try again.' );
|
|
||||||
return $this->index();
|
|
||||||
}
|
|
||||||
Issues::add( 'success', 'Subscriber added.' );
|
|
||||||
$this->index();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function index( $data = null ) {
|
|
||||||
Views::view( 'subscribe.admin.list', self::$subscribe->listPaginated() );
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,79 +0,0 @@
|
|||||||
<?php
|
|
||||||
/**
|
|
||||||
* app/plugins/subscribe/controllers/subscribe.php
|
|
||||||
*
|
|
||||||
* This is the home controller for the subscribe plugin.
|
|
||||||
*
|
|
||||||
* @package TP Subscribe
|
|
||||||
* @version 5.0.1
|
|
||||||
* @author Joey Kimsey <Joey@thetempusproject.com>
|
|
||||||
* @link https://TheTempusProject.com
|
|
||||||
* @license https://opensource.org/licenses/MIT [MIT LICENSE]
|
|
||||||
*/
|
|
||||||
namespace TheTempusProject\Controllers;
|
|
||||||
|
|
||||||
use TheTempusProject\Classes\Controller;
|
|
||||||
use TheTempusProject\Classes\Forms;
|
|
||||||
use TheTempusProject\Hermes\Functions\Redirect;
|
|
||||||
use TheTempusProject\Bedrock\Functions\Session;
|
|
||||||
use TheTempusProject\Bedrock\Functions\Input;
|
|
||||||
use TheTempusProject\Bedrock\Functions\Check;
|
|
||||||
use TheTempusProject\Classes\Email;
|
|
||||||
use TheTempusProject\Houdini\Classes\Issues;
|
|
||||||
use TheTempusProject\Houdini\Classes\Views;
|
|
||||||
use TheTempusProject\Models\Subscribe as SubscribeModel;
|
|
||||||
|
|
||||||
class Subscribe extends Controller {
|
|
||||||
private static $subscribe;
|
|
||||||
|
|
||||||
public function __construct() {
|
|
||||||
parent::__construct();
|
|
||||||
self::$subscribe = new SubscribeModel;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function index() {
|
|
||||||
self::$title = 'Subscribe - {SITENAME}';
|
|
||||||
self::$pageDescription = 'We are always publishing great content and keeping our members up to date. If you would like to join our list, you can subscribe here.';
|
|
||||||
if ( !Input::exists( 'email' ) ) {
|
|
||||||
return Views::view( 'subscribe.subscribe' );
|
|
||||||
}
|
|
||||||
if ( !Forms::check( 'subscribe' ) ) {
|
|
||||||
Issues::add( 'error', [ 'There was an error with your form.' => Check::userErrors() ] );
|
|
||||||
return Views::view( 'subscribe.subscribe' );
|
|
||||||
}
|
|
||||||
if ( !self::$subscribe->add( Input::post( 'email' ) ) ) {
|
|
||||||
Issues::add( 'error', 'There was an error with your request, please try again.' );
|
|
||||||
return Views::view( 'subscribe.subscribe' );
|
|
||||||
}
|
|
||||||
$data = self::$subscribe->get( Input::post( 'email' ) );
|
|
||||||
Email::send( Input::post( 'email' ), 'subscribe', $data->confirmationCode, ['template' => true] );
|
|
||||||
Issues::add( 'success', 'You have successfully been subscribed to our mailing list.' );
|
|
||||||
}
|
|
||||||
|
|
||||||
public function unsubscribe( $email = null, $code = null ) {
|
|
||||||
self::$title = '{SITENAME}';
|
|
||||||
self::$pageDescription = '';
|
|
||||||
if ( !empty( $email ) && !empty( $code ) ) {
|
|
||||||
if ( self::$subscribe->unsubscribe( $email, $code ) ) {
|
|
||||||
return Issues::add( 'success', 'You have been successfully unsubscribed from receiving further mailings.' );
|
|
||||||
}
|
|
||||||
Issues::add( 'error', 'There was an error with your request.' );
|
|
||||||
return Views::view( 'subscribe.unsubscribe' );
|
|
||||||
}
|
|
||||||
if ( !Input::exists( 'submit' ) ) {
|
|
||||||
return Views::view( 'subscribe.unsubscribe' );
|
|
||||||
}
|
|
||||||
if ( !Forms::check( 'unsubscribe' ) ) {
|
|
||||||
Issues::add( 'error', [ 'There was an error with your request.' => Check::userErrors() ] );
|
|
||||||
return Views::view( 'subscribe.unsubscribe' );
|
|
||||||
}
|
|
||||||
$data = self::$subscribe->get( Input::post( 'email' ) );
|
|
||||||
if ( empty( $data ) ) {
|
|
||||||
Issues::add( 'notice', 'There was an error with your request.' );
|
|
||||||
return Views::view( 'subscribe.unsubscribe' );
|
|
||||||
}
|
|
||||||
Email::send( Input::post( 'email' ), 'unsubInstructions', $data->confirmationCode, ['template' => true] );
|
|
||||||
Session::flash( 'success', 'An email with instructions on how to unsubscribe has been sent to your email.' );
|
|
||||||
Redirect::to( 'home/index' );
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,73 +0,0 @@
|
|||||||
<?php
|
|
||||||
/**
|
|
||||||
* app/plugins/subscribe/forms.php
|
|
||||||
*
|
|
||||||
* This houses all of the form checking functions for this plugin.
|
|
||||||
*
|
|
||||||
* @package TP Subscribe
|
|
||||||
* @version 5.0.1
|
|
||||||
* @author Joey Kimsey <Joey@thetempusproject.com>
|
|
||||||
* @link https://TheTempusProject.com
|
|
||||||
* @license https://opensource.org/licenses/MIT [MIT LICENSE]
|
|
||||||
*/
|
|
||||||
namespace TheTempusProject\Plugins\Subscribe;
|
|
||||||
|
|
||||||
use TheTempusProject\Bedrock\Functions\Input;
|
|
||||||
use TheTempusProject\Classes\Forms;
|
|
||||||
|
|
||||||
class SubscribeForms extends Forms {
|
|
||||||
/**
|
|
||||||
* Adds these functions to the form list.
|
|
||||||
*/
|
|
||||||
public function __construct() {
|
|
||||||
self::addHandler( 'subscribe', __CLASS__, 'subscribe' );
|
|
||||||
self::addHandler( 'unsubscribe', __CLASS__, 'unsubscribe' );
|
|
||||||
self::addHandler( 'newSubscription', __CLASS__, 'newSubscription' );
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Validates the subscribe form.
|
|
||||||
*
|
|
||||||
* @return {bool}
|
|
||||||
*/
|
|
||||||
public static function subscribe() {
|
|
||||||
if ( !self::email( Input::post( 'email' ) ) ) {
|
|
||||||
self::addUserError( 'Invalid email.' );
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if ( !self::token() ) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Validates the unsubscribe form.
|
|
||||||
*
|
|
||||||
* @return {bool}
|
|
||||||
*/
|
|
||||||
public static function unsubscribe() {
|
|
||||||
if ( !self::email( Input::post( 'email' ) ) ) {
|
|
||||||
self::addUserError( 'Invalid email.' );
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if ( !self::token() ) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Validates the new subscription form.
|
|
||||||
*
|
|
||||||
* @return {bool}
|
|
||||||
*/
|
|
||||||
public static function newSubscription() {
|
|
||||||
if ( !self::token() ) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
new SubscribeForms;
|
|
@ -1,97 +0,0 @@
|
|||||||
<?php
|
|
||||||
/**
|
|
||||||
* app/plugins/subscribe/models/subscribe.php
|
|
||||||
*
|
|
||||||
* This class is used for the manipulation of the subscribers database table.
|
|
||||||
*
|
|
||||||
* @package TP Subscribe
|
|
||||||
* @version 5.0.1
|
|
||||||
* @author Joey Kimsey <Joey@thetempusproject.com>
|
|
||||||
* @link https://TheTempusProject.com
|
|
||||||
* @license https://opensource.org/licenses/MIT [MIT LICENSE]
|
|
||||||
*/
|
|
||||||
namespace TheTempusProject\Models;
|
|
||||||
|
|
||||||
use TheTempusProject\Bedrock\Functions\Check;
|
|
||||||
use TheTempusProject\Bedrock\Functions\Code;
|
|
||||||
use TheTempusProject\Canary\Bin\Canary as Debug;
|
|
||||||
use TheTempusProject\Classes\DatabaseModel;
|
|
||||||
|
|
||||||
class Subscribe extends DatabaseModel {
|
|
||||||
public $tableName = 'subscribers';
|
|
||||||
public $databaseMatrix = [
|
|
||||||
[ 'confirmed', 'int', '1' ],
|
|
||||||
[ 'subscribed', 'int', '10' ],
|
|
||||||
[ 'confirmationCode', 'varchar', '80' ],
|
|
||||||
[ 'email', 'varchar', '75' ],
|
|
||||||
];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds an email to the subscribers database.
|
|
||||||
*
|
|
||||||
* @param string $email - the email you are trying to add.
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public function add( $email ) {
|
|
||||||
if ( !Check::email( $email ) ) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
$alreadyExists = self::$db->get( $this->tableName, ['email', '=', $email] );
|
|
||||||
|
|
||||||
if ( $alreadyExists->error() ) {
|
|
||||||
Debug::info( 'Error querying database: ' . $alreadyExists->errorMessage() );
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( $alreadyExists->count() ) {
|
|
||||||
Debug::info( 'email already subscribed.' );
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
$fields = [
|
|
||||||
'email' => $email,
|
|
||||||
'confirmationCode' => Code::genConfirmation(),
|
|
||||||
'confirmed' => 0,
|
|
||||||
'subscribed' => time(),
|
|
||||||
];
|
|
||||||
self::$db->insert( $this->tableName, $fields );
|
|
||||||
return self::$db->lastId();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes an email from the subscribers database.
|
|
||||||
*
|
|
||||||
* @param string $email - The email you are trying to remove.
|
|
||||||
* @param string $code - The confirmation code to unsubscribe.
|
|
||||||
* @return boolean
|
|
||||||
*/
|
|
||||||
public function unsubscribe( $email, $code ) {
|
|
||||||
if ( !Check::email( $email ) ) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
$user = self::$db->get( $this->tableName, ['email', '=', $email, 'AND', 'confirmationCode', '=', $code] );
|
|
||||||
if ( !$user->count() ) {
|
|
||||||
Debug::info( __METHOD__ . ' - Cannot find subscriber with that email and code' );
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
self::$db->delete( $this->tableName, ['ID', '=', $user->first()->ID] );
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a subscriber object for the provided email address.
|
|
||||||
*
|
|
||||||
* @param string $email - An email address to look for.
|
|
||||||
* @return bool|object - Depending on success.
|
|
||||||
*/
|
|
||||||
public function get( $email ) {
|
|
||||||
if ( !Check::email( $email ) ) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
$data = self::$db->get( $this->tableName, ['email', '=', $email] );
|
|
||||||
if ( !$data->count() ) {
|
|
||||||
Debug::info( __METHOD__ . ' - Email not found' );
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return (object) $data->first();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,47 +0,0 @@
|
|||||||
<?php
|
|
||||||
/**
|
|
||||||
* app/plugins/subscribe/plugin.php
|
|
||||||
*
|
|
||||||
* This houses all of the main plugin info and functionality.
|
|
||||||
*
|
|
||||||
* @package TP Subscribe
|
|
||||||
* @version 5.0.1
|
|
||||||
* @author Joey Kimsey <Joey@thetempusproject.com>
|
|
||||||
* @link https://TheTempusProject.com
|
|
||||||
* @license https://opensource.org/licenses/MIT [MIT LICENSE]
|
|
||||||
*/
|
|
||||||
namespace TheTempusProject\Plugins;
|
|
||||||
|
|
||||||
use ReflectionClass;
|
|
||||||
use TheTempusProject\Classes\Installer;
|
|
||||||
use TheTempusProject\Houdini\Classes\Navigation;
|
|
||||||
use TheTempusProject\Classes\Plugin;
|
|
||||||
use TheTempusProject\Houdini\Classes\Components;
|
|
||||||
use TheTempusProject\Houdini\Classes\Views;
|
|
||||||
use TheTempusProject\TheTempusProject as App;
|
|
||||||
|
|
||||||
class Subscribe extends Plugin {
|
|
||||||
private static $loaded = false;
|
|
||||||
public $pluginName = 'TP Subscribe';
|
|
||||||
public $pluginAuthor = 'JoeyK';
|
|
||||||
public $pluginWebsite = 'https://TheTempusProject.com';
|
|
||||||
public $modelVersion = '1.0';
|
|
||||||
public $pluginVersion = '3.0';
|
|
||||||
public $pluginDescription = 'A simple plugin to add a method for users to share their email.';
|
|
||||||
public $admin_links = [
|
|
||||||
[
|
|
||||||
'text' => '<i class="fa fa-fw fa-address-book"></i> Subscriptions',
|
|
||||||
'url' => '{ROOT_URL}admin/subscriptions',
|
|
||||||
],
|
|
||||||
];
|
|
||||||
|
|
||||||
public function __construct( $load = false ) {
|
|
||||||
parent::__construct( $load );
|
|
||||||
if ( ! self::$loaded ) {
|
|
||||||
if ( $this->checkEnabled() ) {
|
|
||||||
Components::append( 'FOOTER_RIGHT', Views::simpleView( 'subscribe.footer.right') );
|
|
||||||
}
|
|
||||||
self::$loaded = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,24 +0,0 @@
|
|||||||
<div class="context-main-bg context-main p-3">
|
|
||||||
<legend class="text-center">Add Subscriber</legend>
|
|
||||||
<hr>
|
|
||||||
{ADMIN_BREADCRUMBS}
|
|
||||||
<form method="post">
|
|
||||||
<fieldset>
|
|
||||||
<!-- Subject -->
|
|
||||||
<div class="mb-3 row">
|
|
||||||
<label for="email" class="col-lg-5 col-form-label text-end">Email:</label>
|
|
||||||
<div class="col-lg-3">
|
|
||||||
<input type="email" class="form-control" name="email" id="email" required>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Hidden Token -->
|
|
||||||
<input type="hidden" name="token" value="{TOKEN}">
|
|
||||||
</fieldset>
|
|
||||||
|
|
||||||
<!-- Submit Button -->
|
|
||||||
<div class="text-center">
|
|
||||||
<button type="submit" name="submit" value="submit" class="btn btn-primary btn-lg center-block">Submit</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
@ -1,40 +0,0 @@
|
|||||||
<div class="context-main-bg context-main p-3">
|
|
||||||
<legend class="text-center">Subscribers</legend>
|
|
||||||
<hr>
|
|
||||||
{ADMIN_BREADCRUMBS}
|
|
||||||
<form action="{ROOT_URL}admin/subscriptions/delete" method="post">
|
|
||||||
<table class="table table-striped">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th style="width: 5%">ID</th>
|
|
||||||
<th style="width: 85%">email</th>
|
|
||||||
<th style="width: 5%"></th>
|
|
||||||
<th style="width: 5%">
|
|
||||||
<input type="checkbox" onchange="checkAll(this)" name="check.s" value="S_[]">
|
|
||||||
</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{LOOP}
|
|
||||||
<tr>
|
|
||||||
<td align="center">{ID}</td>
|
|
||||||
<td>{EMAIL}</td>
|
|
||||||
<td><a href="{ROOT_URL}admin/subscriptions/delete/{ID}" class="btn btn-sm btn-danger"><i class="fa fa-fw fa-trash"></i></a></td>
|
|
||||||
<td>
|
|
||||||
<input type="checkbox" value="{ID}" name="S_[]">
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
{/LOOP}
|
|
||||||
{ALT}
|
|
||||||
<tr>
|
|
||||||
<td align="center" colspan="6">
|
|
||||||
No results to show.
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
{/ALT}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
<a href="{ROOT_URL}admin/subscriptions/add" class="btn btn-sm btn-primary">Add</a>
|
|
||||||
<button name="submit" value="submit" type="submit" class="btn btn-sm btn-danger"><i class="fa fa-fw fa-trash"></i></button>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
@ -1,11 +0,0 @@
|
|||||||
<div class="col-12 col-sm-6 col-md-3 offset-0 offset-lg-2 mb-3 text-center">
|
|
||||||
<h5 class="">Subscribe</h5>
|
|
||||||
<div class="d-flex flex-column w-75 w-sm-100 gap-2 justify-content-center mx-auto">
|
|
||||||
<form action="{ROOT_URL}subscribe/home" method="post">
|
|
||||||
<label for="email" class="visually-hidden">Email address</label>
|
|
||||||
<input name="email" id="email" type="email" class="form-control my-2" placeholder="Email address" autocomplete="email">
|
|
||||||
<input type="hidden" name="token" value="{TOKEN}">
|
|
||||||
<button class="btn btn-primary my-2 w-100" name="submit" value="submit" type="submit">Subscribe</button>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
@ -1,5 +0,0 @@
|
|||||||
<form action="{ROOT_URL}subscribe/home" method="post">
|
|
||||||
<input type="email" placeholder="Email" id="email" name="email" autocomplete="email">
|
|
||||||
<input type="hidden" name="token" value="{TOKEN}">
|
|
||||||
<button name="submit" value="submit" type="submit" class="btn btn-lg btn-primary center-block">Submit</button><br>
|
|
||||||
</form>
|
|
@ -1,5 +0,0 @@
|
|||||||
<form action="{ROOT_URL}home/unsubscribe" method="post">
|
|
||||||
<input type="email" placeholder="Email" id="email" name="email">
|
|
||||||
<input type="hidden" name="token" value="{TOKEN}">
|
|
||||||
<button name="submit" value="submit" type="submit" class="btn btn-lg btn-primary center-block">Submit</button><br>
|
|
||||||
</form>
|
|
85
app/plugins/updates/controllers/updates.php
Normal file
85
app/plugins/updates/controllers/updates.php
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* app/plugins/updates/controllers/updates.php
|
||||||
|
*
|
||||||
|
* This is the status updates controller.
|
||||||
|
*
|
||||||
|
* @package TP Status-Updates
|
||||||
|
* @version 3.0
|
||||||
|
* @author Joey Kimsey <Joey@thetempusproject.com>
|
||||||
|
* @link https://TheTempusProject.com
|
||||||
|
* @license https://opensource.org/licenses/MIT [MIT LICENSE]
|
||||||
|
*/
|
||||||
|
namespace TheTempusProject\Controllers;
|
||||||
|
|
||||||
|
use TheTempusProject\Hermes\Functions\Redirect;
|
||||||
|
use TheTempusProject\Bedrock\Functions\Check;
|
||||||
|
use TheTempusProject\Bedrock\Functions\Input;
|
||||||
|
use TheTempusProject\Bedrock\Functions\Session;
|
||||||
|
use TheTempusProject\Houdini\Classes\Issues;
|
||||||
|
use TheTempusProject\Houdini\Classes\Views;
|
||||||
|
use TheTempusProject\Classes\Controller;
|
||||||
|
use TheTempusProject\Classes\Forms;
|
||||||
|
use TheTempusProject\Models\Update;
|
||||||
|
use TheTempusProject\TheTempusProject as App;
|
||||||
|
use TheTempusProject\Houdini\Classes\Components;
|
||||||
|
|
||||||
|
class Updates extends Controller {
|
||||||
|
protected static $updates;
|
||||||
|
|
||||||
|
public function __construct() {
|
||||||
|
parent::__construct();
|
||||||
|
self::$updates = new Update;
|
||||||
|
self::$title = 'Status Updates - {SITENAME}';
|
||||||
|
self::$pageDescription = 'On this page you can view and post new status updates.';
|
||||||
|
if ( ! App::$isLoggedIn ) {
|
||||||
|
return Issues::add( 'notice', 'You must be logged in to post or view statuses.' );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function index() {
|
||||||
|
$updates = self::$updates->byUser( App::$activeUser->ID );
|
||||||
|
Components::set( 'list', Views::simpleView( 'updates.list', $updates ) );
|
||||||
|
Components::set( 'create', Views::simpleView( 'updates.create' ) );
|
||||||
|
|
||||||
|
if ( ! Input::exists() ) {
|
||||||
|
return Views::view( 'updates.dash' );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ! Forms::check( 'createUpdate' ) ) {
|
||||||
|
Issues::add( 'error', [ 'There was an error with your report.' => Check::userErrors() ] );
|
||||||
|
return Views::view( 'updates.dash' );
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = self::$updates->create( Input::post( 'status' ) );
|
||||||
|
|
||||||
|
if ( true === $result ) {
|
||||||
|
Issues::add( 'success', 'Your status has been posted.' );
|
||||||
|
} else {
|
||||||
|
Issues::add( 'error', 'There was an unresolved error while submitting your status.' );
|
||||||
|
}
|
||||||
|
|
||||||
|
return Views::view( 'updates.dash' );
|
||||||
|
}
|
||||||
|
|
||||||
|
public function post() {
|
||||||
|
if ( ! Input::exists() ) {
|
||||||
|
return Views::view( 'updates.create' );
|
||||||
|
}
|
||||||
|
if ( !Forms::check( 'createUpdate' ) ) {
|
||||||
|
Issues::add( 'error', [ 'There was an error with your report.' => Check::userErrors() ] );
|
||||||
|
return Views::view( 'updates.create' );
|
||||||
|
}
|
||||||
|
$result = self::$updates->create( Input::post( 'status' ) );
|
||||||
|
if ( true === $result ) {
|
||||||
|
Session::flash( 'success', 'Your status has been posted.' );
|
||||||
|
Redirect::to( 'home/index' );
|
||||||
|
} else {
|
||||||
|
Issues::add( 'error', 'There was an unresolved error while submitting your status.' );
|
||||||
|
return Views::view( 'updates.create' );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function byUser( $id = null ) {
|
||||||
|
}
|
||||||
|
}
|
@ -1,41 +1,36 @@
|
|||||||
<?php
|
<?php
|
||||||
/**
|
/**
|
||||||
* app/plugins/notifications/forms.php
|
* app/plugins/updates/forms.php
|
||||||
*
|
*
|
||||||
* This houses all of the form checking functions for this plugin.
|
* This houses all of the form checking functions for this plugin.
|
||||||
*
|
*
|
||||||
* @package TP Notifications
|
* @package TP Status-Updates
|
||||||
* @version 5.0.1
|
* @version 5.0.1
|
||||||
* @author Joey Kimsey <Joey@thetempusproject.com>
|
* @author Joey Kimsey <Joey@thetempusproject.com>
|
||||||
* @link https://TheTempusProject.com
|
* @link https://TheTempusProject.com
|
||||||
* @license https://opensource.org/licenses/MIT [MIT LICENSE]
|
* @license https://opensource.org/licenses/MIT [MIT LICENSE]
|
||||||
*/
|
*/
|
||||||
namespace TheTempusProject\Plugins\Notifications;
|
namespace TheTempusProject\Plugins\Updates;
|
||||||
|
|
||||||
use TheTempusProject\Bedrock\Functions\Input;
|
use TheTempusProject\Bedrock\Functions\Input;
|
||||||
use TheTempusProject\Bedrock\Functions\Check;
|
use TheTempusProject\Bedrock\Functions\Check;
|
||||||
use TheTempusProject\Classes\Forms;
|
use TheTempusProject\Classes\Forms;
|
||||||
|
|
||||||
class NotificationForms extends Forms {
|
class UpdateForms extends Forms {
|
||||||
/**
|
/**
|
||||||
* Adds these functions to the form list.
|
* Adds these functions to the form list.
|
||||||
*/
|
*/
|
||||||
public function __construct() {
|
public function __construct() {
|
||||||
self::addHandler( 'createNotification', __CLASS__, 'createNotification' );
|
self::addHandler( 'createUpdate', __CLASS__, 'createUpdate' );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public static function createUpdate() {
|
||||||
* Validates the createNotification form.
|
if ( ! Input::exists( 'status' ) ) {
|
||||||
*
|
Check::addUserError( 'You must provide an status.' );
|
||||||
* @return {bool}
|
|
||||||
*/
|
|
||||||
public static function createNotification() {
|
|
||||||
if ( ! Input::exists( 'notification' ) ) {
|
|
||||||
Check::addUserError( 'You must provide a notification.' );
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
new NotificationForms;
|
new UpdateForms;
|
87
app/plugins/updates/models/update.php
Normal file
87
app/plugins/updates/models/update.php
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* app/plugins/updates/models/update.php
|
||||||
|
*
|
||||||
|
* This class is used for the manipulation of the status_updates database table.
|
||||||
|
*
|
||||||
|
* @package TP Status-Updates
|
||||||
|
* @version 3.0
|
||||||
|
* @author Joey Kimsey <Joey@thetempusproject.com>
|
||||||
|
* @link https://TheTempusProject.com
|
||||||
|
* @license https://opensource.org/licenses/MIT [MIT LICENSE]
|
||||||
|
*/
|
||||||
|
namespace TheTempusProject\Models;
|
||||||
|
|
||||||
|
use TheTempusProject\Bedrock\Classes\Config;
|
||||||
|
use TheTempusProject\Bedrock\Functions\Check;
|
||||||
|
use TheTempusProject\Canary\Bin\Canary as Debug;
|
||||||
|
use TheTempusProject\Classes\DatabaseModel;
|
||||||
|
use TheTempusProject\Plugins\Updates as Plugin;
|
||||||
|
use TheTempusProject\TheTempusProject as App;
|
||||||
|
|
||||||
|
class Update extends DatabaseModel {
|
||||||
|
public $tableName = 'status_updates';
|
||||||
|
public $databaseMatrix = [
|
||||||
|
[ 'status', 'varchar', '512' ],
|
||||||
|
[ 'createdAt', 'int', '10' ],
|
||||||
|
[ 'createdBy', 'int', '10' ],
|
||||||
|
[ 'updatedAt', 'int', '10' ],
|
||||||
|
[ 'updatedBy', 'int', '10' ],
|
||||||
|
[ 'deletedAt', 'int', '10' ],
|
||||||
|
[ 'deletedBy', 'int', '10' ],
|
||||||
|
];
|
||||||
|
public $plugin;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The model constructor.
|
||||||
|
*/
|
||||||
|
public function __construct() {
|
||||||
|
parent::__construct();
|
||||||
|
$this->plugin = new Plugin;
|
||||||
|
}
|
||||||
|
public function create( $status ) {
|
||||||
|
if ( ! $this->plugin->checkEnabled() ) {
|
||||||
|
Debug::info( 'Status-Updates are disabled in the config.' );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
$fields = [
|
||||||
|
'status' => $status,
|
||||||
|
'createdAt' => time(),
|
||||||
|
'createdBy' => App::$activeUser->ID,
|
||||||
|
];
|
||||||
|
if ( !self::$db->insert( $this->tableName, $fields ) ) {
|
||||||
|
Debug::info( 'Status-Updates::create - failed to insert to db' );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return self::$db->lastId();
|
||||||
|
}
|
||||||
|
public function update( $status ) {
|
||||||
|
if ( !$this->plugin->checkEnabled() ) {
|
||||||
|
Debug::info( 'Status-Updates are disabled in the config.' );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
$fields = [
|
||||||
|
'status' => $status,
|
||||||
|
'updatedAt' => time(),
|
||||||
|
'updatedBy' => App::$activeUser->ID,
|
||||||
|
];
|
||||||
|
if ( !self::$db->update( $this->tableName, $id, $fields ) ) {
|
||||||
|
Debug::info( 'Status-Updates::update - failed to update to db' );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
public function byUser( $userID, $limit = null ) {
|
||||||
|
$whereClause = [ 'createdBy', '=', $userID ];
|
||||||
|
if ( empty( $limit ) ) {
|
||||||
|
$reviews = self::$db->get( $this->tableName, $whereClause );
|
||||||
|
} else {
|
||||||
|
$reviews = self::$db->get( $this->tableName, $whereClause, 'ID', 'DESC', [0, $limit] );
|
||||||
|
}
|
||||||
|
if ( !$reviews->count() ) {
|
||||||
|
Debug::info( 'No Reviews found.' );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return $this->filter( $reviews->results() );
|
||||||
|
}
|
||||||
|
}
|
30
app/plugins/updates/plugin.php
Normal file
30
app/plugins/updates/plugin.php
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* app/plugins/updates/plugin.php
|
||||||
|
*
|
||||||
|
* This houses all of the main plugin info and functionality.
|
||||||
|
*
|
||||||
|
* @package TP Status-Updates
|
||||||
|
* @version 3.0
|
||||||
|
* @author Joey Kimsey <Joey@thetempusproject.com>
|
||||||
|
* @link https://TheTempusProject.com
|
||||||
|
* @license https://opensource.org/licenses/MIT [MIT LICENSE]
|
||||||
|
*/
|
||||||
|
namespace TheTempusProject\Plugins;
|
||||||
|
|
||||||
|
use TheTempusProject\Classes\Plugin;
|
||||||
|
|
||||||
|
class Updates extends Plugin {
|
||||||
|
public $pluginName = 'TP Status-Updates';
|
||||||
|
public $pluginAuthor = 'JoeyK';
|
||||||
|
public $pluginWebsite = 'https://TheTempusProject.com';
|
||||||
|
public $modelVersion = '1.0';
|
||||||
|
public $pluginVersion = '3.0';
|
||||||
|
public $pluginDescription = 'A simple plugin which adds a user status update system.';
|
||||||
|
public $permissionMatrix = [
|
||||||
|
'updates' => [
|
||||||
|
'pretty' => 'Can create status updates',
|
||||||
|
'default' => false,
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
10
app/plugins/updates/views/create.html
Normal file
10
app/plugins/updates/views/create.html
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<div class="container mt-5">
|
||||||
|
<h2>Update your status</h2>
|
||||||
|
<form id="status-form" method="post">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="status">Your Status:</label>
|
||||||
|
<textarea class="form-control" id="status" name="status" rows="5" required></textarea>
|
||||||
|
</div>
|
||||||
|
<button name="submit" value="submit" type="submit" class="btn btn-lg btn-primary center-block ">Submit</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
2
app/plugins/updates/views/dash.html
Normal file
2
app/plugins/updates/views/dash.html
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
{list}
|
||||||
|
{create}
|
26
app/plugins/updates/views/list.html
Normal file
26
app/plugins/updates/views/list.html
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
<legend>Updates</legend>
|
||||||
|
<table class="table table-striped">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th style="width: 20%">Date</th>
|
||||||
|
<th style="width: 70%">Status</th>
|
||||||
|
<th style="width: 10%"></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{LOOP}
|
||||||
|
<tr>
|
||||||
|
<td align="center">{DTC}{createdAt}{/DTC}</td>
|
||||||
|
<td align="center">{status}</td>
|
||||||
|
<td><a href="{ROOT_URL}updates/delete/{ID}" class="btn btn-sm btn-danger" role="button"><i class="glyphicon glyphicon-trash"></i></a></td>
|
||||||
|
</tr>
|
||||||
|
{/LOOP}
|
||||||
|
{ALT}
|
||||||
|
<tr>
|
||||||
|
<td align="center" colspan="6">
|
||||||
|
No results to show.
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{/ALT}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
100
app/plugins/wip/controllers/admin/wip.php
Normal file
100
app/plugins/wip/controllers/admin/wip.php
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* app/plugins/wip/controllers/admin/wip.php
|
||||||
|
*
|
||||||
|
* This is the projects admin controller.
|
||||||
|
*
|
||||||
|
* @package TP Wip
|
||||||
|
* @version 3.0
|
||||||
|
* @author Joey Kimsey <Joey@thetempusproject.com>
|
||||||
|
* @link https://TheTempusProject.com
|
||||||
|
* @license https://opensource.org/licenses/MIT [MIT LICENSE]
|
||||||
|
*/
|
||||||
|
namespace TheTempusProject\Controllers\Admin;
|
||||||
|
|
||||||
|
use TheTempusProject\Bedrock\Functions\Check;
|
||||||
|
use TheTempusProject\Bedrock\Functions\Input;
|
||||||
|
use TheTempusProject\Houdini\Classes\Issues;
|
||||||
|
use TheTempusProject\Houdini\Classes\Views;
|
||||||
|
use TheTempusProject\Houdini\Classes\Navigation;
|
||||||
|
use TheTempusProject\Houdini\Classes\Components;
|
||||||
|
use TheTempusProject\Classes\AdminController;
|
||||||
|
use TheTempusProject\Classes\Forms;
|
||||||
|
use TheTempusProject\Models\Projects;
|
||||||
|
|
||||||
|
class Wip extends AdminController {
|
||||||
|
public static $projects;
|
||||||
|
|
||||||
|
public function __construct() {
|
||||||
|
parent::__construct();
|
||||||
|
self::$projects = new Projects;
|
||||||
|
self::$title = 'Admin - WIP';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function index( $data = null ) {
|
||||||
|
Views::view( 'wip.admin.list', self::$projects->listPaginated() );
|
||||||
|
}
|
||||||
|
|
||||||
|
public function create( $data = null ) {
|
||||||
|
if ( !Input::exists( 'submit' ) ) {
|
||||||
|
return Views::view( 'wip.admin.create' );
|
||||||
|
}
|
||||||
|
if ( !Forms::check( 'newProject' ) ) {
|
||||||
|
Issues::add( 'error', [ 'There was an error with your request.' => Check::userErrors() ] );
|
||||||
|
return $this->index();
|
||||||
|
}
|
||||||
|
$result = self::$projects->create( Input::post( 'title' ), Input::post( 'description' ), Input::post( 'progress' ), Input::post( 'startDate' ) );
|
||||||
|
if ( $result ) {
|
||||||
|
Issues::add( 'success', 'Your projects has been created.' );
|
||||||
|
return $this->index();
|
||||||
|
} else {
|
||||||
|
Issues::add( 'error', [ 'There was an unknown error submitting your data.' => Check::userErrors() ] );
|
||||||
|
return $this->index();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function edit( $data = null ) {
|
||||||
|
if ( !Input::exists( 'submit' ) ) {
|
||||||
|
return Views::view( 'wip.admin.edit', self::$projects->findById( $data ) );
|
||||||
|
}
|
||||||
|
if ( !Forms::check( 'editProject' ) ) {
|
||||||
|
Issues::add( 'error', [ 'There was an error with your form.' => Check::userErrors() ] );
|
||||||
|
return $this->index();
|
||||||
|
}
|
||||||
|
$fields = [
|
||||||
|
'title' => Input::post( 'title' ),
|
||||||
|
'description' => Input::post( 'description' ),
|
||||||
|
'progress' => Input::post( 'progress' ),
|
||||||
|
'startDate' => Input::post( 'startDate' ),
|
||||||
|
];
|
||||||
|
if ( self::$projects->update( $data, $fields ) ) {
|
||||||
|
Issues::add( 'success', 'Projects Updated.' );
|
||||||
|
return $this->index();
|
||||||
|
}
|
||||||
|
Issues::add( 'error', 'There was an error with your request.' );
|
||||||
|
$this->index();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function view( $data = null ) {
|
||||||
|
$projectData = self::$projects->findById( $data );
|
||||||
|
if ( $projectData !== false ) {
|
||||||
|
return Views::view( 'wip.admin.view', $projectData );
|
||||||
|
}
|
||||||
|
Issues::add( 'error', 'Projects not found.' );
|
||||||
|
$this->index();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function delete( $data = null ) {
|
||||||
|
if ( $data == null ) {
|
||||||
|
if ( Input::exists( 'P_' ) ) {
|
||||||
|
$data = Input::post( 'P_' );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ( !self::$projects->delete( (array) $data ) ) {
|
||||||
|
Issues::add( 'error', 'There was an error with your request.' );
|
||||||
|
} else {
|
||||||
|
Issues::add( 'success', 'Projects has been deleted' );
|
||||||
|
}
|
||||||
|
$this->index();
|
||||||
|
}
|
||||||
|
}
|
36
app/plugins/wip/controllers/wip.php
Normal file
36
app/plugins/wip/controllers/wip.php
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* app/plugins/wip/controllers/wip.php
|
||||||
|
*
|
||||||
|
* This is the bug reports controller.
|
||||||
|
*
|
||||||
|
* @package TP Wip
|
||||||
|
* @version 3.0
|
||||||
|
* @author Joey Kimsey <Joey@thetempusproject.com>
|
||||||
|
* @link https://TheTempusProject.com
|
||||||
|
* @license https://opensource.org/licenses/MIT [MIT LICENSE]
|
||||||
|
*/
|
||||||
|
namespace TheTempusProject\Controllers;
|
||||||
|
|
||||||
|
use TheTempusProject\Houdini\Classes\Issues;
|
||||||
|
use TheTempusProject\Houdini\Classes\Views;
|
||||||
|
use TheTempusProject\Classes\Controller;
|
||||||
|
use TheTempusProject\Models\Projects;
|
||||||
|
|
||||||
|
class Wip extends Controller {
|
||||||
|
protected static $projects;
|
||||||
|
|
||||||
|
public function index() {
|
||||||
|
self::$projects = new Projects;
|
||||||
|
self::$title = '{SITENAME} - Works in Progress';
|
||||||
|
self::$pageDescription = 'Its not the longest wip in the world, but I\'m certainly proud of it.';
|
||||||
|
|
||||||
|
$projects = self::$projects->listPaginated();
|
||||||
|
if ( false == $projects ) {
|
||||||
|
Issues::add( 'error', 'Well, this is embarrassing, surely he wouldn\'t just have no wip..... right.... Dave? ... erm Joey?' );
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
Views::view( 'wip.wip', self::$projects->listPaginated() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
79
app/plugins/wip/forms.php
Normal file
79
app/plugins/wip/forms.php
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* app/plugins/wip/forms.php
|
||||||
|
*
|
||||||
|
* This houses all of the form checking functions for this plugin.
|
||||||
|
*
|
||||||
|
* @package TP Wip
|
||||||
|
* @version 3.0
|
||||||
|
* @author Joey Kimsey <Joey@thetempusproject.com>
|
||||||
|
* @link https://TheTempusProject.com
|
||||||
|
* @license https://opensource.org/licenses/MIT [MIT LICENSE]
|
||||||
|
*/
|
||||||
|
namespace TheTempusProject\Plugins\Wip;
|
||||||
|
|
||||||
|
use TheTempusProject\Bedrock\Functions\Input;
|
||||||
|
use TheTempusProject\Bedrock\Functions\Check;
|
||||||
|
use TheTempusProject\Classes\Forms;
|
||||||
|
|
||||||
|
class WipForms extends Forms {
|
||||||
|
/**
|
||||||
|
* Adds these functions to the form list.
|
||||||
|
*/
|
||||||
|
public function __construct() {
|
||||||
|
self::addHandler( 'newProject', __CLASS__, 'newProject' );
|
||||||
|
self::addHandler( 'editProject', __CLASS__, 'editProject' );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validates the new project post form.
|
||||||
|
*
|
||||||
|
* @return {bool}
|
||||||
|
*/
|
||||||
|
public static function newProject() {
|
||||||
|
if ( !Input::exists( 'title' ) ) {
|
||||||
|
self::addUserError( 'You must specify title' );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if ( !Input::exists( 'description' ) ) {
|
||||||
|
self::addUserError( 'You must specify description' );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if ( !Input::exists( 'progress' ) ) {
|
||||||
|
self::addUserError( 'You must specify progress' );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if ( !Input::exists( 'startDate' ) ) {
|
||||||
|
self::addUserError( 'You must specify startDate' );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validates the edit project post form.
|
||||||
|
*
|
||||||
|
* @return {bool}
|
||||||
|
*/
|
||||||
|
public static function editProject() {
|
||||||
|
if ( !Input::exists( 'title' ) ) {
|
||||||
|
self::addUserError( 'You must specify title' );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if ( !Input::exists( 'description' ) ) {
|
||||||
|
self::addUserError( 'You must specify description' );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if ( !Input::exists( 'progress' ) ) {
|
||||||
|
self::addUserError( 'You must specify progress' );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if ( !Input::exists( 'startDate' ) ) {
|
||||||
|
self::addUserError( 'You must specify startDate' );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
new WipForms;
|
100
app/plugins/wip/models/projects.php
Normal file
100
app/plugins/wip/models/projects.php
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* app/plugins/wip/models/projects.php
|
||||||
|
*
|
||||||
|
* This class is used for the manipulation of the projects database table.
|
||||||
|
*
|
||||||
|
* @package TP WIP
|
||||||
|
* @version 3.0
|
||||||
|
* @author Joey Kimsey <Joey@thetempusproject.com>
|
||||||
|
* @link https://TheTempusProject.com
|
||||||
|
* @license https://opensource.org/licenses/MIT [MIT LICENSE]
|
||||||
|
*/
|
||||||
|
namespace TheTempusProject\Models;
|
||||||
|
|
||||||
|
use TheTempusProject\Bedrock\Classes\Config;
|
||||||
|
use TheTempusProject\Bedrock\Functions\Check;
|
||||||
|
use TheTempusProject\Canary\Bin\Canary as Debug;
|
||||||
|
use TheTempusProject\Classes\DatabaseModel;
|
||||||
|
use TheTempusProject\Plugins\Wip as Plugin;
|
||||||
|
use DateTime;
|
||||||
|
|
||||||
|
class Projects extends DatabaseModel {
|
||||||
|
public $tableName = 'projects';
|
||||||
|
public $databaseMatrix = [
|
||||||
|
[ 'title', 'varchar', '128' ],
|
||||||
|
[ 'description', 'text', '' ],
|
||||||
|
[ 'progress', 'int', '3' ],
|
||||||
|
[ 'startDate', 'varchar', '32' ],
|
||||||
|
[ 'endDate', 'varchar', '32' ],
|
||||||
|
];
|
||||||
|
public $plugin;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The model constructor.
|
||||||
|
*/
|
||||||
|
public function __construct() {
|
||||||
|
parent::__construct();
|
||||||
|
$this->plugin = new Plugin;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function create( $title, $description, $progress, $startDate ) {
|
||||||
|
if ( !$this->plugin->checkEnabled() ) {
|
||||||
|
Debug::info( 'Wip is disabled in the config.' );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
$fields = [
|
||||||
|
'title' => $title,
|
||||||
|
'description' => $description,
|
||||||
|
'progress' => $progress,
|
||||||
|
'startDate' => $startDate,
|
||||||
|
];
|
||||||
|
if ( !self::$db->insert( $this->tableName, $fields ) ) {
|
||||||
|
Debug::info( 'Projects::create - failed to insert to db' );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return self::$db->lastId();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function update( $id, $fields ) {
|
||||||
|
if ( !Check::id( $id ) ) {
|
||||||
|
Debug::info( 'Project:update: illegal ID.' );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if ( !self::$db->update( $this->tableName, $id, $fields ) ) {
|
||||||
|
// new CustomException( 'projectUpdate' );
|
||||||
|
Debug::error( "Project:update: $id not updated: $fields" );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function filter( $postArray, $params = [] ) {
|
||||||
|
foreach ( $postArray as $instance ) {
|
||||||
|
if ( !is_object( $instance ) ) {
|
||||||
|
$instance = $postArray;
|
||||||
|
$end = true;
|
||||||
|
}
|
||||||
|
if ( ! empty( $instance->startDate ) ) {
|
||||||
|
$startDate = DateTime::createFromFormat( 'Y-m', $instance->startDate );
|
||||||
|
$startDateFormatted = $startDate->format('F Y');
|
||||||
|
$instance->prettyStart = $startDateFormatted;
|
||||||
|
} else {
|
||||||
|
$instance->prettyStart = 'TBD';
|
||||||
|
}
|
||||||
|
if ( ! empty( $instance->endDate ) ) {
|
||||||
|
$endDate = DateTime::createFromFormat( 'Y-m', $instance->endDate );
|
||||||
|
$endDateFormatted = $endDate->format('F Y');
|
||||||
|
$instance->prettyEnd = $endDateFormatted;
|
||||||
|
} else {
|
||||||
|
$instance->prettyEnd = 'TBD';
|
||||||
|
}
|
||||||
|
$out[] = $instance;
|
||||||
|
if ( !empty( $end ) ) {
|
||||||
|
$out = $out[0];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $out;
|
||||||
|
}
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user