diff --git a/Gruntfile.js b/Gruntfile.js
new file mode 100644
index 0000000..ce2e045
--- /dev/null
+++ b/Gruntfile.js
@@ -0,0 +1,52 @@
+module.exports = function(grunt) {
+
+// Project configuration.
+ grunt.initConfig({
+ pkg: grunt.file.readJSON( 'package.json' ),
+
+// Clean the build folder
+ clean: {
+ build: {
+ src: ['build/**', 'build-zip']
+ }
+ },
+// Copy to build folder
+ copy: {
+ readme: {
+ src: ['README.md'],
+ dest: 'build/'
+ },
+ component: {
+ expand: true,
+ cwd: 'component/',
+ src: ['**'],
+ dest: 'build/'
+ }
+ },
+// Compress the build folder into an upload-ready zip file
+ compress: {
+ component: {
+ options: {
+ archive: 'build-zip/<%= pkg.name %>-<%= pkg.version %>.zip'
+ },
+ files: [
+ {expand: true, cwd: 'build/', src: ['**']}
+ ]
+ }
+ }
+ });
+
+// Load all grunt plugins here
+ grunt.loadNpmTasks('grunt-contrib-copy');
+ grunt.loadNpmTasks('grunt-contrib-clean');
+ grunt.loadNpmTasks('grunt-contrib-compress');
+
+// Build task
+ grunt.registerTask( 'build', [
+ 'clean:build',
+ 'copy:readme',
+ 'copy:component',
+ 'compress:component'
+ ]);
+
+};
\ No newline at end of file
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..23cb790
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,339 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ {description}
+ Copyright (C) {year} {fullname}
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ {signature of Ty Coon}, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..8bb1784
--- /dev/null
+++ b/README.md
@@ -0,0 +1,11 @@
+#Simple Callback Component#
+Компонент административной части для модуля [Simple Callback Module](https://github.com/birdkiwi/mod_simplecallback/)
+Joomla 3.0+
+
+Скачать компонент: [Все версии](https://github.com/birdkiwi/com_simplecallback/releases/)
+
+**Возможности:**
+
+Компонент позволяет просматривать все сохраненные сообщения из модуля Simple Callback Module.
+
+![mod_simplecallback screenshot](http://joomla.startler.ru/images/screenshots/com_simplecallback.png)
\ No newline at end of file
diff --git a/component/administrator/access.xml b/component/administrator/access.xml
new file mode 100755
index 0000000..3e7f3d6
--- /dev/null
+++ b/component/administrator/access.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/component/administrator/assets/css/index.html b/component/administrator/assets/css/index.html
new file mode 100755
index 0000000..42682b4
--- /dev/null
+++ b/component/administrator/assets/css/index.html
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/component/administrator/assets/css/simplecallback.css b/component/administrator/assets/css/simplecallback.css
new file mode 100755
index 0000000..789b63d
--- /dev/null
+++ b/component/administrator/assets/css/simplecallback.css
@@ -0,0 +1,18 @@
+.icon-48-messages {
+ background-image: url(../images/l_messages.png);
+ padding-left:60px!important;
+}
+
+.icon-48-message {
+ background-image: url(../images/l_messages.png);
+ padding-left:60px!important;
+}
+
+.color-box-messages {
+ float: left; width: 15px; height: 15px; margin-right: 5px; border: 1px solid rgba(0, 0, 0, .2);}
+
+
+
+.other-filters{
+ padding: 0 14px;
+}
\ No newline at end of file
diff --git a/component/administrator/assets/images/index.html b/component/administrator/assets/images/index.html
new file mode 100755
index 0000000..42682b4
--- /dev/null
+++ b/component/administrator/assets/images/index.html
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/component/administrator/assets/images/l_com_simplecallback.png b/component/administrator/assets/images/l_com_simplecallback.png
new file mode 100755
index 0000000..f33ac1e
Binary files /dev/null and b/component/administrator/assets/images/l_com_simplecallback.png differ
diff --git a/component/administrator/assets/images/l_messages.png b/component/administrator/assets/images/l_messages.png
new file mode 100755
index 0000000..8280a62
Binary files /dev/null and b/component/administrator/assets/images/l_messages.png differ
diff --git a/component/administrator/assets/images/s_com_simplecallback.png b/component/administrator/assets/images/s_com_simplecallback.png
new file mode 100755
index 0000000..5df9b3c
Binary files /dev/null and b/component/administrator/assets/images/s_com_simplecallback.png differ
diff --git a/component/administrator/assets/images/s_messages.png b/component/administrator/assets/images/s_messages.png
new file mode 100755
index 0000000..84fea81
Binary files /dev/null and b/component/administrator/assets/images/s_messages.png differ
diff --git a/component/administrator/assets/index.html b/component/administrator/assets/index.html
new file mode 100755
index 0000000..42682b4
--- /dev/null
+++ b/component/administrator/assets/index.html
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/component/administrator/config.xml b/component/administrator/config.xml
new file mode 100755
index 0000000..bd3171e
--- /dev/null
+++ b/component/administrator/config.xml
@@ -0,0 +1,93 @@
+
+
+
+
+
+
+
+
+
diff --git a/component/administrator/controller.php b/component/administrator/controller.php
new file mode 100755
index 0000000..18c1199
--- /dev/null
+++ b/component/administrator/controller.php
@@ -0,0 +1,39 @@
+
+ * @copyright 2016 Alexey Startler
+ * @license GNU General Public License version 2 or later; see LICENSE.txt
+ */
+// No direct access
+defined('_JEXEC') or die;
+
+/**
+ * Class SimplecallbackController
+ *
+ * @since 1.6
+ */
+class SimplecallbackController extends JControllerLegacy
+{
+ /**
+ * Method to display a view.
+ *
+ * @param boolean $cachable If true, the view output will be cached
+ * @param mixed $urlparams An array of safe url parameters and their variable types, for valid values see {@link JFilterInput::clean()}.
+ *
+ * @return JController This object to support chaining.
+ *
+ * @since 1.5
+ */
+ public function display($cachable = false, $urlparams = false)
+ {
+ $view = JFactory::getApplication()->input->getCmd('view', 'messages');
+ JFactory::getApplication()->input->set('view', $view);
+
+ parent::display($cachable, $urlparams);
+
+ return $this;
+ }
+}
diff --git a/component/administrator/controllers/index.html b/component/administrator/controllers/index.html
new file mode 100755
index 0000000..42682b4
--- /dev/null
+++ b/component/administrator/controllers/index.html
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/component/administrator/controllers/message.php b/component/administrator/controllers/message.php
new file mode 100755
index 0000000..c4166e4
--- /dev/null
+++ b/component/administrator/controllers/message.php
@@ -0,0 +1,32 @@
+
+ * @copyright 2016 Alexey Startler
+ * @license GNU General Public License version 2 or later; see LICENSE.txt
+ */
+
+// No direct access
+defined('_JEXEC') or die;
+
+jimport('joomla.application.component.controllerform');
+
+/**
+ * Message controller class.
+ *
+ * @since 1.6
+ */
+class SimplecallbackControllerMessage extends JControllerForm
+{
+ /**
+ * Constructor
+ *
+ * @throws Exception
+ */
+ public function __construct()
+ {
+ $this->view_list = 'messages';
+ parent::__construct();
+ }
+}
diff --git a/component/administrator/controllers/messages.php b/component/administrator/controllers/messages.php
new file mode 100755
index 0000000..592800b
--- /dev/null
+++ b/component/administrator/controllers/messages.php
@@ -0,0 +1,107 @@
+
+ * @copyright 2016 Alexey Startler
+ * @license GNU General Public License version 2 or later; see LICENSE.txt
+ */
+
+// No direct access.
+defined('_JEXEC') or die;
+
+jimport('joomla.application.component.controlleradmin');
+
+use Joomla\Utilities\ArrayHelper;
+
+/**
+ * Messages list controller class.
+ *
+ * @since 1.6
+ */
+class SimplecallbackControllerMessages extends JControllerAdmin
+{
+ /**
+ * Method to clone existing Messages
+ *
+ * @return void
+ */
+ public function duplicate()
+ {
+ // Check for request forgeries
+ Jsession::checkToken() or jexit(JText::_('JINVALID_TOKEN'));
+
+ // Get id(s)
+ $pks = $this->input->post->get('cid', array(), 'array');
+
+ try
+ {
+ if (empty($pks))
+ {
+ throw new Exception(JText::_('COM_SIMPLECALLBACK_NO_ELEMENT_SELECTED'));
+ }
+
+ ArrayHelper::toInteger($pks);
+ $model = $this->getModel();
+ $model->duplicate($pks);
+ $this->setMessage(Jtext::_('COM_SIMPLECALLBACK_ITEMS_SUCCESS_DUPLICATED'));
+ }
+ catch (Exception $e)
+ {
+ JFactory::getApplication()->enqueueMessage($e->getMessage(), 'warning');
+ }
+
+ $this->setRedirect('index.php?option=com_simplecallback&view=messages');
+ }
+
+ /**
+ * Proxy for getModel.
+ *
+ * @param string $name Optional. Model name
+ * @param string $prefix Optional. Class prefix
+ * @param array $config Optional. Configuration array for model
+ *
+ * @return object The Model
+ *
+ * @since 1.6
+ */
+ public function getModel($name = 'message', $prefix = 'SimplecallbackModel', $config = array())
+ {
+ $model = parent::getModel($name, $prefix, array('ignore_request' => true));
+
+ return $model;
+ }
+
+ /**
+ * Method to save the submitted ordering values for records via AJAX.
+ *
+ * @return void
+ *
+ * @since 3.0
+ */
+ public function saveOrderAjax()
+ {
+ // Get the input
+ $input = JFactory::getApplication()->input;
+ $pks = $input->post->get('cid', array(), 'array');
+ $order = $input->post->get('order', array(), 'array');
+
+ // Sanitize the input
+ ArrayHelper::toInteger($pks);
+ ArrayHelper::toInteger($order);
+
+ // Get the model
+ $model = $this->getModel();
+
+ // Save the ordering
+ $return = $model->saveorder($pks, $order);
+
+ if ($return)
+ {
+ echo "1";
+ }
+
+ // Close the application
+ JFactory::getApplication()->close();
+ }
+}
diff --git a/component/administrator/helpers/index.html b/component/administrator/helpers/index.html
new file mode 100755
index 0000000..42682b4
--- /dev/null
+++ b/component/administrator/helpers/index.html
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/component/administrator/helpers/listhelper.php b/component/administrator/helpers/listhelper.php
new file mode 100755
index 0000000..675c6cd
--- /dev/null
+++ b/component/administrator/helpers/listhelper.php
@@ -0,0 +1,34 @@
+
+ * @copyright 2016 Alexey Startler
+ * @license GNU General Public License version 2 or later; see LICENSE.txt
+ */
+// No direct access
+defined('_JEXEC') or die;
+
+/**
+ * Simplecallback Listhelper.
+ *
+ * @since 1.6
+ */
+abstract class JHtmlListhelper
+{
+ static function toggle($value = 0, $view, $field, $i)
+ {
+ $states = array(
+ 0 => array('icon-remove', JText::_('Toggle'), 'inactive btn-danger'),
+ 1 => array('icon-checkmark', JText::_('Toggle'), 'active btn-success')
+ );
+
+ $state = \Joomla\Utilities\ArrayHelper::getValue($states, (int) $value, $states[0]);
+ $text = '';
+ $html = '' . $text . '';
+
+ return $html;
+ }
+}
diff --git a/component/administrator/helpers/simplecallback.php b/component/administrator/helpers/simplecallback.php
new file mode 100755
index 0000000..2b48dba
--- /dev/null
+++ b/component/administrator/helpers/simplecallback.php
@@ -0,0 +1,94 @@
+
+ * @copyright 2016 Alexey Startler
+ * @license GNU General Public License version 2 or later; see LICENSE.txt
+ */
+// No direct access
+defined('_JEXEC') or die;
+
+/**
+ * Simplecallback helper.
+ *
+ * @since 1.6
+ */
+class SimplecallbackHelpersSimplecallback
+{
+ /**
+ * Configure the Linkbar.
+ *
+ * @param string $vName string
+ *
+ * @return void
+ */
+ public static function addSubmenu($vName = '')
+ {
+ JHtmlSidebar::addEntry(
+ JText::_('COM_SIMPLECALLBACK_TITLE_MESSAGES'),
+ 'index.php?option=com_simplecallback&view=messages',
+ $vName == 'messages'
+ );
+
+ }
+
+ /**
+ * Gets the files attached to an item
+ *
+ * @param int $pk The item's id
+ *
+ * @param string $table The table's name
+ *
+ * @param string $field The field's name
+ *
+ * @return array The files
+ */
+ public static function getFiles($pk, $table, $field)
+ {
+ $db = JFactory::getDbo();
+ $query = $db->getQuery(true);
+
+ $query
+ ->select($field)
+ ->from($table)
+ ->where('id = ' . (int) $pk);
+
+ $db->setQuery($query);
+
+ return explode(',', $db->loadResult());
+ }
+
+ /**
+ * Gets a list of the actions that can be performed.
+ *
+ * @return JObject
+ *
+ * @since 1.6
+ */
+ public static function getActions()
+ {
+ $user = JFactory::getUser();
+ $result = new JObject;
+
+ $assetName = 'com_simplecallback';
+
+ $actions = array(
+ 'core.admin', 'core.manage', 'core.create', 'core.edit', 'core.edit.own', 'core.edit.state', 'core.delete'
+ );
+
+ foreach ($actions as $action)
+ {
+ $result->set($action, $user->authorise($action, $assetName));
+ }
+
+ return $result;
+ }
+}
+
+
+class SimplecallbackHelper extends SimplecallbackHelpersSimplecallback
+{
+
+}
diff --git a/component/administrator/index.html b/component/administrator/index.html
new file mode 100755
index 0000000..42682b4
--- /dev/null
+++ b/component/administrator/index.html
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/component/administrator/models/fields/createdby.php b/component/administrator/models/fields/createdby.php
new file mode 100755
index 0000000..1c58639
--- /dev/null
+++ b/component/administrator/models/fields/createdby.php
@@ -0,0 +1,58 @@
+
+ * @copyright 2016 Alexey Startler
+ * @license GNU General Public License version 2 or later; see LICENSE.txt
+ */
+
+defined('JPATH_BASE') or die;
+
+jimport('joomla.form.formfield');
+
+/**
+ * Supports an HTML select list of categories
+ *
+ * @since 1.6
+ */
+class JFormFieldCreatedby extends JFormField
+{
+ /**
+ * The form field type.
+ *
+ * @var string
+ * @since 1.6
+ */
+ protected $type = 'createdby';
+
+ /**
+ * Method to get the field input markup.
+ *
+ * @return string The field input markup.
+ *
+ * @since 1.6
+ */
+ protected function getInput()
+ {
+ // Initialize variables.
+ $html = array();
+
+ // Load user
+ $user_id = $this->value;
+
+ if ($user_id)
+ {
+ $user = JFactory::getUser($user_id);
+ }
+ else
+ {
+ $user = JFactory::getUser();
+ $html[] = '';
+ }
+
+ $html[] = "
" . $user->name . " (" . $user->username . ")
";
+
+ return implode($html);
+ }
+}
diff --git a/component/administrator/models/fields/custom_field.php b/component/administrator/models/fields/custom_field.php
new file mode 100755
index 0000000..ca8cc6a
--- /dev/null
+++ b/component/administrator/models/fields/custom_field.php
@@ -0,0 +1,44 @@
+
+ * @copyright 2016 Alexey Startler
+ * @license GNU General Public License version 2 or later; see LICENSE.txt
+ */
+
+defined('JPATH_BASE') or die;
+
+jimport('joomla.html.html');
+jimport('joomla.form.formfield');
+
+/**
+ * Supports an HTML select list of categories
+ *
+ * @since 1.6
+ */
+class JFormFieldCustomField extends JFormField
+{
+ /**
+ * The form field type.
+ *
+ * @var string
+ * @since 1.6
+ */
+ protected $type = 'text';
+
+ /**
+ * Method to get the field input markup.
+ *
+ * @return string The field input markup.
+ *
+ * @since 1.6
+ */
+ protected function getInput()
+ {
+ // Initialize variables.
+ $html = array();
+
+ return implode($html);
+ }
+}
diff --git a/component/administrator/models/fields/filemultiple.php b/component/administrator/models/fields/filemultiple.php
new file mode 100755
index 0000000..684879f
--- /dev/null
+++ b/component/administrator/models/fields/filemultiple.php
@@ -0,0 +1,43 @@
+
+ * @copyright 2016 Alexey Startler
+ * @license GNU General Public License version 2 or later; see LICENSE.txt
+ */
+
+defined('JPATH_BASE') or die;
+
+jimport('joomla.form.formfield');
+
+/**
+ * Supports an HTML select list of categories
+ *
+ * @since 1.6
+ */
+class JFormFieldFileMultiple extends JFormField
+{
+ /**
+ * The form field type.
+ *
+ * @var string
+ * @since 1.6
+ */
+ protected $type = 'file';
+
+ /**
+ * Method to get the field input markup.
+ *
+ * @return string The field input markup.
+ *
+ * @since 1.6
+ */
+ protected function getInput()
+ {
+ // Initialize variables.
+ $html = '';
+
+ return $html;
+ }
+}
diff --git a/component/administrator/models/fields/foreignkey.php b/component/administrator/models/fields/foreignkey.php
new file mode 100755
index 0000000..e89b273
--- /dev/null
+++ b/component/administrator/models/fields/foreignkey.php
@@ -0,0 +1,168 @@
+
+ * @copyright 2016 Alexey Startler
+ * @license GNU General Public License version 2 or later; see LICENSE.txt
+ */
+defined('JPATH_BASE') or die;
+
+jimport('joomla.form.formfield');
+
+/**
+ * Supports a value from an external table
+ *
+ * @since 1.6
+ */
+class JFormFieldForeignKey extends JFormField
+{
+ /**
+ * The form field type.
+ *
+ * @var string
+ * @since 1.6
+ */
+ protected $type = 'foreignkey';
+
+ private $input_type;
+
+ private $table;
+
+ private $key_field;
+
+ private $value_field;
+
+ /**
+ * Method to get the field input markup.
+ *
+ * @return string The field input markup.
+ *
+ * @since 1.6
+ */
+ protected function getInput()
+ {
+ // Assign field properties.
+ // Type of input the field shows
+ $this->input_type = $this->getAttribute('input_type');
+
+ // Database Table
+ $this->table = $this->getAttribute('table');
+
+ // The field that the field will save on the database
+ $this->key_field = (string) $this->getAttribute('key_field');
+
+ // The column that the field shows in the input
+ $this->value_field = (string) $this->getAttribute('value_field');
+
+ // Flag to identify if the fk_value is multiple
+ $this->value_multiple = (int) $this->getAttribute('value_multiple', 0);
+
+ // Initialize variables.
+ $html = '';
+ $fk_value = '';
+
+ // Load all the field options
+ $db = JFactory::getDbo();
+ $query = $db->getQuery(true);
+
+ // Support for multiple fields on fk_values
+ if ($this->value_multiple == 1)
+ {
+ // Get the fields for multiple value
+ $this->value_fields = (string) $this->getAttribute('value_field_multiple');
+ $this->value_fields = explode(',', $this->value_fields);
+
+ $fk_value = ' CONCAT (';
+
+ foreach ($this->value_fields as $field)
+ {
+ $fk_value .= $db->quoteName($field) . ', \' \', ';
+ }
+
+ $fk_value = substr($fk_value, 0, -7);
+ $fk_value .= ') AS ' . $db->quoteName($this->value_field);
+ }
+ else
+ {
+ $fk_value = $db->quoteName($this->value_field);
+ }
+
+ $query
+ ->select(
+ array(
+ $db->quoteName($this->key_field),
+ $fk_value
+ )
+ )
+ ->from($this->table);
+
+ $db->setQuery($query);
+ $results = $db->loadObjectList();
+
+ $input_options = 'class="' . $this->getAttribute('class') . '"';
+
+ // Depends of the type of input, the field will show a type or another
+ switch ($this->input_type)
+ {
+ case 'list':
+ default:
+ $options = array();
+
+ // Iterate through all the results
+ foreach ($results as $result)
+ {
+ $options[] = JHtml::_('select.option', $result->{$this->key_field}, $result->{$this->value_field});
+ }
+
+ $value = $this->value;
+
+ // If the value is a string -> Only one result
+ if (is_string($value))
+ {
+ $value = array($value);
+ }
+ elseif (is_object($value))
+ {
+ // If the value is an object, let's get its properties.
+ $value = get_object_vars($value);
+ }
+
+ // If the select is multiple
+ if ($this->multiple)
+ {
+ $input_options .= 'multiple="multiple"';
+ }
+ else
+ {
+ array_unshift($options, JHtml::_('select.option', '', ''));
+ }
+
+ $html = JHtml::_('select.genericlist', $options, $this->name, $input_options, 'value', 'text', $value, $this->id);
+ break;
+ }
+
+ return $html;
+ }
+
+ /**
+ * Wrapper method for getting attributes from the form element
+ *
+ * @param string $attr_name Attribute name
+ * @param mixed $default Optional value to return if attribute not found
+ *
+ * @return mixed The value of the attribute if it exists, null otherwise
+ */
+ public function getAttribute($attr_name, $default = null)
+ {
+ if (!empty($this->element[$attr_name]))
+ {
+ return $this->element[$attr_name];
+ }
+ else
+ {
+ return $default;
+ }
+ }
+}
diff --git a/component/administrator/models/fields/index.html b/component/administrator/models/fields/index.html
new file mode 100755
index 0000000..42682b4
--- /dev/null
+++ b/component/administrator/models/fields/index.html
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/component/administrator/models/fields/modifiedby.php b/component/administrator/models/fields/modifiedby.php
new file mode 100755
index 0000000..7c3a624
--- /dev/null
+++ b/component/administrator/models/fields/modifiedby.php
@@ -0,0 +1,46 @@
+
+ * @copyright 2016 Alexey Startler
+ * @license GNU General Public License version 2 or later; see LICENSE.txt
+ */
+
+defined('JPATH_BASE') or die;
+
+jimport('joomla.form.formfield');
+
+/**
+ * Supports an HTML select list of categories
+ *
+ * @since 1.6
+ */
+class JFormFieldModifiedby extends JFormField
+{
+ /**
+ * The form field type.
+ *
+ * @var string
+ * @since 1.6
+ */
+ protected $type = 'modifiedby';
+
+ /**
+ * Method to get the field input markup.
+ *
+ * @return string The field input markup.
+ *
+ * @since 1.6
+ */
+ protected function getInput()
+ {
+ // Initialize variables.
+ $html = array();
+ $user = JFactory::getUser();
+ $html[] = '';
+ $html[] = "
" . $user->name . " (" . $user->username . ")
";
+
+ return implode($html);
+ }
+}
diff --git a/component/administrator/models/fields/timecreated.php b/component/administrator/models/fields/timecreated.php
new file mode 100755
index 0000000..27c496c
--- /dev/null
+++ b/component/administrator/models/fields/timecreated.php
@@ -0,0 +1,60 @@
+
+ * @copyright 2016 Alexey Startler
+ * @license GNU General Public License version 2 or later; see LICENSE.txt
+ */
+
+defined('JPATH_BASE') or die;
+
+jimport('joomla.form.formfield');
+
+/**
+ * Supports an HTML select list of categories
+ *
+ * @since 1.6
+ */
+class JFormFieldTimecreated extends JFormField
+{
+ /**
+ * The form field type.
+ *
+ * @var string
+ * @since 1.6
+ */
+ protected $type = 'timecreated';
+
+ /**
+ * Method to get the field input markup.
+ *
+ * @return string The field input markup.
+ *
+ * @since 1.6
+ */
+ protected function getInput()
+ {
+ // Initialize variables.
+ $html = array();
+
+ $time_created = $this->value;
+
+ if (!strtotime($time_created))
+ {
+ $time_created = JFactory::getDate('now', JFactory::getConfig()->get('offset'))->toSql(true);
+ $html[] = '';
+ }
+
+ $hidden = (boolean) $this->element['hidden'];
+
+ if ($hidden == null || !$hidden)
+ {
+ $jdate = new JDate($time_created);
+ $pretty_date = $jdate->format(JText::_('DATE_FORMAT_LC2'));
+ $html[] = "
" . $pretty_date . "
";
+ }
+
+ return implode($html);
+ }
+}
diff --git a/component/administrator/models/fields/timeupdated.php b/component/administrator/models/fields/timeupdated.php
new file mode 100755
index 0000000..cfd728e
--- /dev/null
+++ b/component/administrator/models/fields/timeupdated.php
@@ -0,0 +1,63 @@
+
+ * @copyright 2016 Alexey Startler
+ * @license GNU General Public License version 2 or later; see LICENSE.txt
+ */
+
+defined('JPATH_BASE') or die;
+
+jimport('joomla.form.formfield');
+
+/**
+ * Supports an HTML select list of categories
+ *
+ * @since 1.6
+ */
+class JFormFieldTimeupdated extends JFormField
+{
+ /**
+ * The form field type.
+ *
+ * @var string
+ * @since 1.6
+ */
+ protected $type = 'timeupdated';
+
+ /**
+ * Method to get the field input markup.
+ *
+ * @return string The field input markup.
+ *
+ * @since 1.6
+ */
+ protected function getInput()
+ {
+ // Initialize variables.
+ $html = array();
+
+ $old_time_updated = $this->value;
+ $hidden = (boolean) $this->element['hidden'];
+
+ if ($hidden == null || !$hidden)
+ {
+ if (!strtotime($old_time_updated))
+ {
+ $html[] = '-';
+ }
+ else
+ {
+ $jdate = new JDate($old_time_updated);
+ $pretty_date = $jdate->format(JText::_('DATE_FORMAT_LC2'));
+ $html[] = "
" . $pretty_date . "
";
+ }
+ }
+
+ $time_updated = JFactory::getDate('now', JFactory::getConfig()->get('offset'))->toSql(true);
+ $html[] = '';
+
+ return implode($html);
+ }
+}
diff --git a/component/administrator/models/forms/index.html b/component/administrator/models/forms/index.html
new file mode 100755
index 0000000..42682b4
--- /dev/null
+++ b/component/administrator/models/forms/index.html
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/component/administrator/models/forms/message.xml b/component/administrator/models/forms/message.xml
new file mode 100755
index 0000000..3915812
--- /dev/null
+++ b/component/administrator/models/forms/message.xml
@@ -0,0 +1,26 @@
+
+
diff --git a/component/administrator/models/index.html b/component/administrator/models/index.html
new file mode 100755
index 0000000..42682b4
--- /dev/null
+++ b/component/administrator/models/index.html
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/component/administrator/models/message.php b/component/administrator/models/message.php
new file mode 100755
index 0000000..bf27a2c
--- /dev/null
+++ b/component/administrator/models/message.php
@@ -0,0 +1,219 @@
+
+ * @copyright 2016 Alexey Startler
+ * @license GNU General Public License version 2 or later; see LICENSE.txt
+ */
+
+// No direct access.
+defined('_JEXEC') or die;
+
+jimport('joomla.application.component.modeladmin');
+
+/**
+ * Simplecallback model.
+ *
+ * @since 1.6
+ */
+class SimplecallbackModelMessage extends JModelAdmin
+{
+ /**
+ * @var string The prefix to use with controller messages.
+ * @since 1.6
+ */
+ protected $text_prefix = 'COM_SIMPLECALLBACK';
+
+ /**
+ * @var string Alias to manage history control
+ * @since 3.2
+ */
+ public $typeAlias = 'com_simplecallback.message';
+
+ /**
+ * @var null Item data
+ * @since 1.6
+ */
+ protected $item = null;
+
+ /**
+ * Returns a reference to the a Table object, always creating it.
+ *
+ * @param string $type The table type to instantiate
+ * @param string $prefix A prefix for the table class name. Optional.
+ * @param array $config Configuration array for model. Optional.
+ *
+ * @return JTable A database object
+ *
+ * @since 1.6
+ */
+ public function getTable($type = 'Message', $prefix = 'SimplecallbackTable', $config = array())
+ {
+ return JTable::getInstance($type, $prefix, $config);
+ }
+
+ /**
+ * Method to get the record form.
+ *
+ * @param array $data An optional array of data for the form to interogate.
+ * @param boolean $loadData True if the form is to load its own data (default case), false if not.
+ *
+ * @return JForm A JForm object on success, false on failure
+ *
+ * @since 1.6
+ */
+ public function getForm($data = array(), $loadData = true)
+ {
+ // Initialise variables.
+ $app = JFactory::getApplication();
+
+ // Get the form.
+ $form = $this->loadForm(
+ 'com_simplecallback.message', 'message',
+ array('control' => 'jform',
+ 'load_data' => $loadData
+ )
+ );
+
+ if (empty($form))
+ {
+ return false;
+ }
+
+ return $form;
+ }
+
+ /**
+ * Method to get the data that should be injected in the form.
+ *
+ * @return mixed The data for the form.
+ *
+ * @since 1.6
+ */
+ protected function loadFormData()
+ {
+ // Check the session for previously entered form data.
+ $data = JFactory::getApplication()->getUserState('com_simplecallback.edit.message.data', array());
+
+ if (empty($data))
+ {
+ if ($this->item === null)
+ {
+ $this->item = $this->getItem();
+ }
+
+ $data = $this->item;
+ }
+
+ return $data;
+ }
+
+ /**
+ * Method to get a single record.
+ *
+ * @param integer $pk The id of the primary key.
+ *
+ * @return mixed Object on success, false on failure.
+ *
+ * @since 1.6
+ */
+ public function getItem($pk = null)
+ {
+ if ($item = parent::getItem($pk))
+ {
+ // Do any procesing on fields here if needed
+ }
+
+ return $item;
+ }
+
+ /**
+ * Method to duplicate an Message
+ *
+ * @param array &$pks An array of primary key IDs.
+ *
+ * @return boolean True if successful.
+ *
+ * @throws Exception
+ */
+ public function duplicate(&$pks)
+ {
+ $user = JFactory::getUser();
+
+ // Access checks.
+ if (!$user->authorise('core.create', 'com_simplecallback'))
+ {
+ throw new Exception(JText::_('JERROR_CORE_CREATE_NOT_PERMITTED'));
+ }
+
+ $dispatcher = JEventDispatcher::getInstance();
+ $context = $this->option . '.' . $this->name;
+
+ // Include the plugins for the save events.
+ JPluginHelper::importPlugin($this->events_map['save']);
+
+ $table = $this->getTable();
+
+ foreach ($pks as $pk)
+ {
+ if ($table->load($pk, true))
+ {
+ // Reset the id to create a new record.
+ $table->id = 0;
+
+ if (!$table->check())
+ {
+ throw new Exception($table->getError());
+ }
+
+
+ // Trigger the before save event.
+ $result = $dispatcher->trigger($this->event_before_save, array($context, &$table, true));
+
+ if (in_array(false, $result, true) || !$table->store())
+ {
+ throw new Exception($table->getError());
+ }
+
+ // Trigger the after save event.
+ $dispatcher->trigger($this->event_after_save, array($context, &$table, true));
+ }
+ else
+ {
+ throw new Exception($table->getError());
+ }
+ }
+
+ // Clean cache
+ $this->cleanCache();
+
+ return true;
+ }
+
+ /**
+ * Prepare and sanitise the table prior to saving.
+ *
+ * @param JTable $table Table Object
+ *
+ * @return void
+ *
+ * @since 1.6
+ */
+ protected function prepareTable($table)
+ {
+ jimport('joomla.filter.output');
+
+ if (empty($table->id))
+ {
+ // Set ordering to the last item if not set
+ if (@$table->ordering === '')
+ {
+ $db = JFactory::getDbo();
+ $db->setQuery('SELECT MAX(ordering) FROM #__simplecallback_messages');
+ $max = $db->loadResult();
+ $table->ordering = $max + 1;
+ }
+ }
+ }
+}
diff --git a/component/administrator/models/messages.php b/component/administrator/models/messages.php
new file mode 100755
index 0000000..900e1cf
--- /dev/null
+++ b/component/administrator/models/messages.php
@@ -0,0 +1,191 @@
+
+ * @copyright 2016 Alexey Startler
+ * @license GNU General Public License version 2 or later; see LICENSE.txt
+ */
+defined('_JEXEC') or die;
+
+jimport('joomla.application.component.modellist');
+
+/**
+ * Methods supporting a list of Simplecallback records.
+ *
+ * @since 1.6
+ */
+class SimplecallbackModelMessages extends JModelList
+{
+/**
+ * Constructor.
+ *
+ * @param array $config An optional associative array of configuration settings.
+ *
+ * @see JController
+ * @since 1.6
+ */
+ public function __construct($config = array())
+ {
+ if (empty($config['filter_fields']))
+ {
+ $config['filter_fields'] = array(
+ 'id', 'a.`id`',
+ 'ordering', 'a.`ordering`',
+ 'state', 'a.`state`',
+ 'created_by', 'a.`created_by`',
+ 'modified_by', 'a.`modified_by`',
+ 'name', 'a.`name`',
+ 'phone', 'a.`phone`',
+ 'message', 'a.`message`',
+ 'ip', 'a.`ip`',
+ 'page', 'a.`page`',
+ 'custom', 'a.`custom`',
+ 'created', 'a.`created`',
+ 'updated', 'a.`updated`',
+ );
+ }
+
+ parent::__construct($config);
+ }
+
+ /**
+ * Method to auto-populate the model state.
+ *
+ * Note. Calling getState in this method will result in recursion.
+ *
+ * @param string $ordering Elements order
+ * @param string $direction Order direction
+ *
+ * @return void
+ *
+ * @throws Exception
+ */
+ protected function populateState($ordering = null, $direction = null)
+ {
+ // Initialise variables.
+ $app = JFactory::getApplication('administrator');
+
+ // Load the filter state.
+ $search = $app->getUserStateFromRequest($this->context . '.filter.search', 'filter_search');
+ $this->setState('filter.search', $search);
+
+ $published = $app->getUserStateFromRequest($this->context . '.filter.state', 'filter_published', '', 'string');
+ $this->setState('filter.state', $published);
+
+ // Load the parameters.
+ $params = JComponentHelper::getParams('com_simplecallback');
+ $this->setState('params', $params);
+
+ // List state information.
+ parent::populateState('a.name', 'asc');
+ }
+
+ /**
+ * Method to get a store id based on model configuration state.
+ *
+ * This is necessary because the model is used by the component and
+ * different modules that might need different sets of data or different
+ * ordering requirements.
+ *
+ * @param string $id A prefix for the store id.
+ *
+ * @return string A store id.
+ *
+ * @since 1.6
+ */
+ protected function getStoreId($id = '')
+ {
+ // Compile the store id.
+ $id .= ':' . $this->getState('filter.search');
+ $id .= ':' . $this->getState('filter.state');
+
+ return parent::getStoreId($id);
+ }
+
+ /**
+ * Build an SQL query to load the list data.
+ *
+ * @return JDatabaseQuery
+ *
+ * @since 1.6
+ */
+ protected function getListQuery()
+ {
+ // Create a new query object.
+ $db = $this->getDbo();
+ $query = $db->getQuery(true);
+
+ // Select the required fields from the table.
+ $query->select(
+ $this->getState(
+ 'list.select', 'DISTINCT a.*'
+ )
+ );
+ $query->from('`#__simplecallback_messages` AS a');
+
+ // Join over the users for the checked out user
+ $query->select("uc.name AS editor");
+ $query->join("LEFT", "#__users AS uc ON uc.id=a.checked_out");
+
+ // Join over the user field 'created_by'
+ $query->select('`created_by`.name AS `created_by`');
+ $query->join('LEFT', '#__users AS `created_by` ON `created_by`.id = a.`created_by`');
+
+ // Join over the user field 'modified_by'
+ $query->select('`modified_by`.name AS `modified_by`');
+ $query->join('LEFT', '#__users AS `modified_by` ON `modified_by`.id = a.`modified_by`');
+
+ // Filter by published state
+ $published = $this->getState('filter.state');
+
+ if (is_numeric($published))
+ {
+ $query->where('a.state = ' . (int) $published);
+ }
+ elseif ($published === '')
+ {
+ $query->where('(a.state IN (0, 1))');
+ }
+
+ // Filter by search in title
+ $search = $this->getState('filter.search');
+
+ if (!empty($search))
+ {
+ if (stripos($search, 'id:') === 0)
+ {
+ $query->where('a.id = ' . (int) substr($search, 3));
+ }
+ else
+ {
+ $search = $db->Quote('%' . $db->escape($search, true) . '%');
+ $query->where('( a.name LIKE ' . $search . ' OR a.phone LIKE ' . $search . ' OR a.ip LIKE ' . $search . ' )');
+ }
+ }
+
+ // Add the list ordering clause.
+ $orderCol = $this->state->get('list.ordering');
+ $orderDirn = $this->state->get('list.direction');
+
+ if ($orderCol && $orderDirn)
+ {
+ $query->order($db->escape($orderCol . ' ' . $orderDirn));
+ }
+
+ return $query;
+ }
+
+ /**
+ * Get an array of data items
+ *
+ * @return mixed Array of data items on success, false on failure.
+ */
+ public function getItems()
+ {
+ $items = parent::getItems();
+
+ return $items;
+ }
+}
diff --git a/component/administrator/simplecallback.php b/component/administrator/simplecallback.php
new file mode 100755
index 0000000..1cf5b0c
--- /dev/null
+++ b/component/administrator/simplecallback.php
@@ -0,0 +1,26 @@
+
+ * @copyright 2016 Alexey Startler
+ * @license GNU General Public License version 2 or later; see LICENSE.txt
+ */
+
+// No direct access
+defined('_JEXEC') or die;
+
+// Access check.
+if (!JFactory::getUser()->authorise('core.manage', 'com_simplecallback'))
+{
+ throw new Exception(JText::_('JERROR_ALERTNOAUTHOR'));
+}
+
+// Include dependancies
+jimport('joomla.application.component.controller');
+
+JLoader::registerPrefix('Simplecallback', JPATH_COMPONENT_ADMINISTRATOR);
+
+$controller = JControllerLegacy::getInstance('Simplecallback');
+$controller->execute(JFactory::getApplication()->input->get('task'));
+$controller->redirect();
diff --git a/component/administrator/sql/index.html b/component/administrator/sql/index.html
new file mode 100755
index 0000000..42682b4
--- /dev/null
+++ b/component/administrator/sql/index.html
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/component/administrator/sql/install.mysql.utf8.sql b/component/administrator/sql/install.mysql.utf8.sql
new file mode 100755
index 0000000..4730822
--- /dev/null
+++ b/component/administrator/sql/install.mysql.utf8.sql
@@ -0,0 +1,26 @@
+CREATE TABLE IF NOT EXISTS `#__simplecallback_messages` (
+`id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT,
+
+`ordering` INT(11) NOT NULL ,
+`state` TINYINT(1) NOT NULL ,
+`checked_out` INT(11) NOT NULL ,
+`checked_out_time` DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00',
+`created_by` INT(11) NOT NULL ,
+`modified_by` INT(11) NOT NULL ,
+`name` VARCHAR(255) NOT NULL ,
+`phone` VARCHAR(255) NOT NULL ,
+`message` VARCHAR(255) NOT NULL ,
+`ip` VARCHAR(255) NOT NULL ,
+`page` VARCHAR(255) NOT NULL ,
+`custom` VARCHAR(255) NOT NULL ,
+`created` DATETIME NOT NULL ,
+`updated` DATETIME NOT NULL ,
+PRIMARY KEY (`id`)
+) DEFAULT COLLATE=utf8mb4_unicode_ci;
+
+
+INSERT INTO `#__content_types` (`type_title`, `type_alias`, `table`, `content_history_options`)
+SELECT * FROM ( SELECT 'Message','com_simplecallback.message','{"special":{"dbtable":"#__simplecallback_messages","key":"id","type":"Message","prefix":"Simple CallbackTable"}}', '{"formFile":"administrator\/components\/com_simplecallback\/models\/forms\/message.xml", "hideFields":["checked_out","checked_out_time","params","language"], "ignoreChanges":["modified_by", "modified", "checked_out", "checked_out_time"], "convertToInt":["publish_up", "publish_down"], "displayLookup":[{"sourceColumn":"catid","targetTable":"#__categories","targetColumn":"id","displayColumn":"title"},{"sourceColumn":"group_id","targetTable":"#__usergroups","targetColumn":"id","displayColumn":"title"},{"sourceColumn":"created_by","targetTable":"#__users","targetColumn":"id","displayColumn":"name"},{"sourceColumn":"access","targetTable":"#__viewlevels","targetColumn":"id","displayColumn":"title"},{"sourceColumn":"modified_by","targetTable":"#__users","targetColumn":"id","displayColumn":"name"}]}') AS tmp
+WHERE NOT EXISTS (
+ SELECT type_alias FROM `#__content_types` WHERE (`type_alias` = 'com_simplecallback.message')
+) LIMIT 1;
diff --git a/component/administrator/sql/uninstall.mysql.utf8.sql b/component/administrator/sql/uninstall.mysql.utf8.sql
new file mode 100755
index 0000000..03c1b04
--- /dev/null
+++ b/component/administrator/sql/uninstall.mysql.utf8.sql
@@ -0,0 +1,3 @@
+DROP TABLE IF EXISTS `#__simplecallback_messages`;
+
+DELETE FROM `#__content_types` WHERE (type_alias LIKE 'com_simplecallback.%');
\ No newline at end of file
diff --git a/component/administrator/sql/update.mysql.utf8.sql b/component/administrator/sql/update.mysql.utf8.sql
new file mode 100755
index 0000000..85ecc6e
--- /dev/null
+++ b/component/administrator/sql/update.mysql.utf8.sql
@@ -0,0 +1 @@
+/* Only premium users are allowed to update a component */
\ No newline at end of file
diff --git a/component/administrator/tables/index.html b/component/administrator/tables/index.html
new file mode 100755
index 0000000..42682b4
--- /dev/null
+++ b/component/administrator/tables/index.html
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/component/administrator/tables/message.php b/component/administrator/tables/message.php
new file mode 100755
index 0000000..2f7ca05
--- /dev/null
+++ b/component/administrator/tables/message.php
@@ -0,0 +1,297 @@
+
+ * @copyright 2016 Alexey Startler
+ * @license GNU General Public License version 2 or later; see LICENSE.txt
+ */
+// No direct access
+defined('_JEXEC') or die;
+
+use Joomla\Utilities\ArrayHelper;
+/**
+ * message Table class
+ *
+ * @since 1.6
+ */
+class SimplecallbackTablemessage extends JTable
+{
+
+ /**
+ * Constructor
+ *
+ * @param JDatabase &$db A database connector object
+ */
+ public function __construct(&$db)
+ {
+ JObserverMapper::addObserverClassToClass('JTableObserverContenthistory', 'SimplecallbackTablemessage', array('typeAlias' => 'com_simplecallback.message'));
+ parent::__construct('#__simplecallback_messages', 'id', $db);
+ }
+
+ /**
+ * Overloaded bind function to pre-process the params.
+ *
+ * @param array $array Named array
+ * @param mixed $ignore Optional array or list of parameters to ignore
+ *
+ * @return null|string null is operation was satisfactory, otherwise returns an error
+ *
+ * @see JTable:bind
+ * @since 1.5
+ */
+ public function bind($array, $ignore = '')
+ {
+ $input = JFactory::getApplication()->input;
+ $task = $input->getString('task', '');
+
+ if ($array['id'] == 0)
+ {
+ $array['created_by'] = JFactory::getUser()->id;
+ }
+
+ if ($array['id'] == 0)
+ {
+ $array['modified_by'] = JFactory::getUser()->id;
+ }
+
+ if ($array['id'] == 0)
+ {
+ $array['created'] = JFactory::getDate()->toSql();
+ }
+ $task = JFactory::getApplication()->input->get('task');
+
+ if ($task == 'apply' || $task == 'save')
+
+ {
+ $array['updated'] = JFactory::getDate()->toSql();
+ }
+
+ if (isset($array['params']) && is_array($array['params']))
+ {
+ $registry = new JRegistry;
+ $registry->loadArray($array['params']);
+ $array['params'] = (string) $registry;
+ }
+
+ if (isset($array['metadata']) && is_array($array['metadata']))
+ {
+ $registry = new JRegistry;
+ $registry->loadArray($array['metadata']);
+ $array['metadata'] = (string) $registry;
+ }
+
+ if (!JFactory::getUser()->authorise('core.admin', 'com_simplecallback.message.' . $array['id']))
+ {
+ $actions = JAccess::getActionsFromFile(
+ JPATH_ADMINISTRATOR . '/components/com_simplecallback/access.xml',
+ "/access/section[@name='message']/"
+ );
+ $default_actions = JAccess::getAssetRules('com_simplecallback.message.' . $array['id'])->getData();
+ $array_jaccess = array();
+
+ foreach ($actions as $action)
+ {
+ $array_jaccess[$action->name] = $default_actions[$action->name];
+ }
+
+ $array['rules'] = $this->JAccessRulestoArray($array_jaccess);
+ }
+
+ // Bind the rules for ACL where supported.
+ if (isset($array['rules']) && is_array($array['rules']))
+ {
+ $this->setRules($array['rules']);
+ }
+
+ return parent::bind($array, $ignore);
+ }
+
+ /**
+ * This function convert an array of JAccessRule objects into an rules array.
+ *
+ * @param array $jaccessrules An array of JAccessRule objects.
+ *
+ * @return array
+ */
+ private function JAccessRulestoArray($jaccessrules)
+ {
+ $rules = array();
+
+ foreach ($jaccessrules as $action => $jaccess)
+ {
+ $actions = array();
+
+ if ($jaccess)
+ {
+ foreach ($jaccess->getData() as $group => $allow)
+ {
+ $actions[$group] = ((bool)$allow);
+ }
+ }
+
+ $rules[$action] = $actions;
+ }
+
+ return $rules;
+ }
+
+ /**
+ * Overloaded check function
+ *
+ * @return bool
+ */
+ public function check()
+ {
+ // If there is an ordering column and this is a new row then get the next ordering value
+ if (property_exists($this, 'ordering') && $this->id == 0)
+ {
+ $this->ordering = self::getNextOrder();
+ }
+
+
+
+ return parent::check();
+ }
+
+ /**
+ * Method to set the publishing state for a row or list of rows in the database
+ * table. The method respects checked out rows by other users and will attempt
+ * to checkin rows that it can after adjustments are made.
+ *
+ * @param mixed $pks An optional array of primary key values to update. If not
+ * set the instance property value is used.
+ * @param integer $state The publishing state. eg. [0 = unpublished, 1 = published]
+ * @param integer $userId The user id of the user performing the operation.
+ *
+ * @return boolean True on success.
+ *
+ * @since 1.0.4
+ *
+ * @throws Exception
+ */
+ public function publish($pks = null, $state = 1, $userId = 0)
+ {
+ // Initialise variables.
+ $k = $this->_tbl_key;
+
+ // Sanitize input.
+ ArrayHelper::toInteger($pks);
+ $userId = (int) $userId;
+ $state = (int) $state;
+
+ // If there are no primary keys set check to see if the instance key is set.
+ if (empty($pks))
+ {
+ if ($this->$k)
+ {
+ $pks = array($this->$k);
+ }
+ // Nothing to set publishing state on, return false.
+ else
+ {
+ throw new Exception(500, JText::_('JLIB_DATABASE_ERROR_NO_ROWS_SELECTED'));
+ }
+ }
+
+ // Build the WHERE clause for the primary keys.
+ $where = $k . '=' . implode(' OR ' . $k . '=', $pks);
+
+ // Determine if there is checkin support for the table.
+ if (!property_exists($this, 'checked_out') || !property_exists($this, 'checked_out_time'))
+ {
+ $checkin = '';
+ }
+ else
+ {
+ $checkin = ' AND (checked_out = 0 OR checked_out = ' . (int) $userId . ')';
+ }
+
+ // Update the publishing state for rows with the given primary keys.
+ $this->_db->setQuery(
+ 'UPDATE `' . $this->_tbl . '`' .
+ ' SET `state` = ' . (int) $state .
+ ' WHERE (' . $where . ')' .
+ $checkin
+ );
+ $this->_db->execute();
+
+ // If checkin is supported and all rows were adjusted, check them in.
+ if ($checkin && (count($pks) == $this->_db->getAffectedRows()))
+ {
+ // Checkin each row.
+ foreach ($pks as $pk)
+ {
+ $this->checkin($pk);
+ }
+ }
+
+ // If the JTable instance value is in the list of primary keys that were set, set the instance.
+ if (in_array($this->$k, $pks))
+ {
+ $this->state = $state;
+ }
+
+ return true;
+ }
+
+ /**
+ * Define a namespaced asset name for inclusion in the #__assets table
+ *
+ * @return string The asset name
+ *
+ * @see JTable::_getAssetName
+ */
+ protected function _getAssetName()
+ {
+ $k = $this->_tbl_key;
+
+ return 'com_simplecallback.message.' . (int) $this->$k;
+ }
+
+ /**
+ * Returns the parent asset's id. If you have a tree structure, retrieve the parent's id using the external key field
+ *
+ * @param JTable $table Table name
+ * @param integer $id Id
+ *
+ * @see JTable::_getAssetParentId
+ *
+ * @return mixed The id on success, false on failure.
+ */
+ protected function _getAssetParentId(JTable $table = null, $id = null)
+ {
+ // We will retrieve the parent-asset from the Asset-table
+ $assetParent = JTable::getInstance('Asset');
+
+ // Default: if no asset-parent can be found we take the global asset
+ $assetParentId = $assetParent->getRootId();
+
+ // The item has the component as asset-parent
+ $assetParent->loadByName('com_simplecallback');
+
+ // Return the found asset-parent-id
+ if ($assetParent->id)
+ {
+ $assetParentId = $assetParent->id;
+ }
+
+ return $assetParentId;
+ }
+
+ /**
+ * Delete a record by id
+ *
+ * @param mixed $pk Primary key value to delete. Optional
+ *
+ * @return bool
+ */
+ public function delete($pk = null)
+ {
+ $this->load($pk);
+ $result = parent::delete($pk);
+
+ return $result;
+ }
+}
diff --git a/component/administrator/views/index.html b/component/administrator/views/index.html
new file mode 100755
index 0000000..42682b4
--- /dev/null
+++ b/component/administrator/views/index.html
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/component/administrator/views/message/index.html b/component/administrator/views/message/index.html
new file mode 100755
index 0000000..42682b4
--- /dev/null
+++ b/component/administrator/views/message/index.html
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/component/administrator/views/message/tmpl/edit.php b/component/administrator/views/message/tmpl/edit.php
new file mode 100755
index 0000000..e99cead
--- /dev/null
+++ b/component/administrator/views/message/tmpl/edit.php
@@ -0,0 +1,107 @@
+
+ * @copyright 2016 Alexey Startler
+ * @license GNU General Public License version 2 or later; see LICENSE.txt
+ */
+// No direct access
+defined('_JEXEC') or die;
+
+JHtml::addIncludePath(JPATH_COMPONENT . '/helpers/html');
+JHtml::_('behavior.tooltip');
+JHtml::_('behavior.formvalidation');
+JHtml::_('formbehavior.chosen', 'select');
+JHtml::_('behavior.keepalive');
+
+// Import CSS
+$document = JFactory::getDocument();
+$document->addStyleSheet(JUri::root() . 'media/com_simplecallback/css/form.css');
+?>
+
+
+
diff --git a/component/administrator/views/message/tmpl/index.html b/component/administrator/views/message/tmpl/index.html
new file mode 100755
index 0000000..42682b4
--- /dev/null
+++ b/component/administrator/views/message/tmpl/index.html
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/component/administrator/views/message/view.html.php b/component/administrator/views/message/view.html.php
new file mode 100755
index 0000000..3619c6d
--- /dev/null
+++ b/component/administrator/views/message/view.html.php
@@ -0,0 +1,112 @@
+
+ * @copyright 2016 Alexey Startler
+ * @license GNU General Public License version 2 or later; see LICENSE.txt
+ */
+// No direct access
+defined('_JEXEC') or die;
+
+jimport('joomla.application.component.view');
+
+/**
+ * View to edit
+ *
+ * @since 1.6
+ */
+class SimplecallbackViewMessage extends JViewLegacy
+{
+ protected $state;
+
+ protected $item;
+
+ protected $form;
+
+ /**
+ * Display the view
+ *
+ * @param string $tpl Template name
+ *
+ * @return void
+ *
+ * @throws Exception
+ */
+ public function display($tpl = null)
+ {
+ $this->state = $this->get('State');
+ $this->item = $this->get('Item');
+ $this->form = $this->get('Form');
+
+ // Check for errors.
+ if (count($errors = $this->get('Errors')))
+ {
+ throw new Exception(implode("\n", $errors));
+ }
+
+ $this->addToolbar();
+ parent::display($tpl);
+ }
+
+ /**
+ * Add the page title and toolbar.
+ *
+ * @return void
+ *
+ * @throws Exception
+ */
+ protected function addToolbar()
+ {
+ JFactory::getApplication()->input->set('hidemainmenu', true);
+
+ $user = JFactory::getUser();
+ $isNew = ($this->item->id == 0);
+
+ if (isset($this->item->checked_out))
+ {
+ $checkedOut = !($this->item->checked_out == 0 || $this->item->checked_out == $user->get('id'));
+ }
+ else
+ {
+ $checkedOut = false;
+ }
+
+ $canDo = SimplecallbackHelpersSimplecallback::getActions();
+
+ JToolBarHelper::title(JText::_('COM_SIMPLECALLBACK_TITLE_MESSAGE'), 'message.png');
+
+ // If not checked out, can save the item.
+ if (!$checkedOut && ($canDo->get('core.edit') || ($canDo->get('core.create'))))
+ {
+ JToolBarHelper::apply('message.apply', 'JTOOLBAR_APPLY');
+ JToolBarHelper::save('message.save', 'JTOOLBAR_SAVE');
+ }
+
+ if (!$checkedOut && ($canDo->get('core.create')))
+ {
+ JToolBarHelper::custom('message.save2new', 'save-new.png', 'save-new_f2.png', 'JTOOLBAR_SAVE_AND_NEW', false);
+ }
+
+ // If an existing item, can save to a copy.
+ if (!$isNew && $canDo->get('core.create'))
+ {
+ JToolBarHelper::custom('message.save2copy', 'save-copy.png', 'save-copy_f2.png', 'JTOOLBAR_SAVE_AS_COPY', false);
+ }
+
+ // Button for version control
+ if ($this->state->params->get('save_history', 1) && $user->authorise('core.edit')) {
+ JToolbarHelper::versions('com_simplecallback.message', $this->item->id);
+ }
+
+ if (empty($this->item->id))
+ {
+ JToolBarHelper::cancel('message.cancel', 'JTOOLBAR_CANCEL');
+ }
+ else
+ {
+ JToolBarHelper::cancel('message.cancel', 'JTOOLBAR_CLOSE');
+ }
+ }
+}
diff --git a/component/administrator/views/messages/index.html b/component/administrator/views/messages/index.html
new file mode 100755
index 0000000..42682b4
--- /dev/null
+++ b/component/administrator/views/messages/index.html
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/component/administrator/views/messages/tmpl/default.php b/component/administrator/views/messages/tmpl/default.php
new file mode 100755
index 0000000..f217cdf
--- /dev/null
+++ b/component/administrator/views/messages/tmpl/default.php
@@ -0,0 +1,302 @@
+
+ * @copyright 2016 Alexey Startler
+ * @license GNU General Public License version 2 or later; see LICENSE.txt
+ */
+
+// No direct access
+defined('_JEXEC') or die;
+
+JHtml::addIncludePath(JPATH_COMPONENT . '/helpers/');
+JHtml::_('bootstrap.tooltip');
+JHtml::_('behavior.multiselect');
+JHtml::_('formbehavior.chosen', 'select');
+
+// Import CSS
+$document = JFactory::getDocument();
+$document->addStyleSheet(JUri::root() . 'administrator/components/com_simplecallback/assets/css/simplecallback.css');
+$document->addStyleSheet(JUri::root() . 'media/com_simplecallback/css/list.css');
+
+$user = JFactory::getUser();
+$userId = $user->get('id');
+$listOrder = $this->state->get('list.ordering');
+$listDirn = $this->state->get('list.direction');
+$canOrder = $user->authorise('core.edit.state', 'com_simplecallback');
+$saveOrder = $listOrder == 'a.`ordering`';
+
+if ($saveOrder)
+{
+ $saveOrderingUrl = 'index.php?option=com_simplecallback&task=messages.saveOrderAjax&tmpl=component';
+ JHtml::_('sortablelist.sortable', 'messageList', 'adminForm', strtolower($listDirn), $saveOrderingUrl);
+}
+
+$sortFields = $this->getSortFields();
+?>
+
+
+extra_sidebar))
+{
+ $this->sidebar .= $this->extra_sidebar;
+}
+
+?>
+
+