summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNiklas Olmes <niklas@olmes.de>2026-04-24 19:30:00 +0200
committerNiklas Olmes <niklas@olmes.de>2026-04-24 19:30:00 +0200
commitcdea8caa5617f0cb77bcbc9803759abd2df50644 (patch)
tree2f7f1bd3af3b2396baf5403ad1a7ad00bcb7fae9
stipcrmHEADmain
-rw-r--r--.htaccess4
-rw-r--r--LICENSE339
-rw-r--r--Makefile18
-rw-r--r--addcomm.php32
-rw-r--r--addtemplate.php42
-rw-r--r--allocation.php59
-rw-r--r--alumni.pers.php64
-rw-r--r--alumni.php19
-rw-r--r--auto_common.php180
-rw-r--r--autoactions.php48
-rw-r--r--autoeemail.php31
-rw-r--r--autoemail.php80
-rw-r--r--autoform.php646
-rw-r--r--autopdf.php79
-rw-r--r--autotable.php1103
-rw-r--r--autotable_explain.php36
-rw-r--r--autotable_fontsettings.php41
-rw-r--r--calls.php65
-rw-r--r--commission.php74
-rw-r--r--commissioners.php161
-rw-r--r--commissioners_person.php113
-rw-r--r--commissions.php61
-rw-r--r--composer.json9
-rw-r--r--contract.php111
-rw-r--r--contracts.php19
-rw-r--r--contracts_common.php146
-rw-r--r--curfutpatrons.php19
-rw-r--r--curfutpatronspersons.php27
-rw-r--r--curpatrons.php19
-rw-r--r--curstips.php19
-rw-r--r--curstipspers.php58
-rw-r--r--delay.php22
-rw-r--r--delcomm.php20
-rw-r--r--delcontract.php20
-rw-r--r--delcontractc.php140
-rw-r--r--delcontractdo.php41
-rw-r--r--deldocument.php46
-rw-r--r--deldonation.php20
-rw-r--r--delorga.php284
-rw-r--r--delorgado.php41
-rw-r--r--delorgaperson.php20
-rw-r--r--delpatron.php179
-rw-r--r--delpatrondo.php41
-rw-r--r--delperson.php385
-rw-r--r--delpersondo.php70
-rw-r--r--delstip.php133
-rw-r--r--delstipdo.php41
-rw-r--r--deltemplate.php37
-rw-r--r--donation.php206
-rw-r--r--donations.php197
-rw-r--r--donationsXdonation.php40
-rw-r--r--donationsXorga.php43
-rw-r--r--donationsXpers.php39
-rw-r--r--dopdf.php202
-rw-r--r--email.php708
-rw-r--r--emailhtml.php454
-rw-r--r--form.scm989
-rw-r--r--futstips.php19
-rw-r--r--handle_css.php18
-rw-r--r--handle_js.php18
-rw-r--r--lang-tags.scm8
-rw-r--r--orga.php83
-rw-r--r--orgaXpers.php49
-rw-r--r--orgapersons.php142
-rw-r--r--orgas.php42
-rw-r--r--patron.php224
-rw-r--r--patrons.php19
-rw-r--r--patrons_by_year.php41
-rw-r--r--patrons_common.php155
-rw-r--r--patronspersons.php28
-rw-r--r--patronspersons_by_year.php42
-rw-r--r--patronspersons_common.php115
-rw-r--r--pdf.php516
-rw-r--r--pers.php177
-rw-r--r--persXorga.php130
-rw-r--r--persons.php50
-rw-r--r--php.scm66
-rw-r--r--robots.txt2
-rw-r--r--searchorga.php64
-rw-r--r--searchpatron.php67
-rw-r--r--searchperson.php100
-rw-r--r--searchstip.php20
-rw-r--r--sendmail.php267
-rw-r--r--stips.php29
-rw-r--r--stipspers.php57
-rw-r--r--template.email.php63
-rw-r--r--templater_common.php567
-rw-r--r--templates.email.php72
-rwxr-xr-xunwrap-php.sh5
-rw-r--r--update.php95
90 files changed, 11490 insertions, 0 deletions
diff --git a/.htaccess b/.htaccess
new file mode 100644
index 0000000..15f94b7
--- /dev/null
+++ b/.htaccess
@@ -0,0 +1,4 @@
+Options -Indexes
+
+RewriteEngine On
+RewriteRule .* - [F,L,NC]
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..d511905
--- /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.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ 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/Makefile b/Makefile
new file mode 100644
index 0000000..bc545b7
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,18 @@
+%.php: %.scm
+ @echo Creating $@
+ @(./speakhtml.scm $< > $@ && ( tidy -q --indent yes --wrap 0 --vertical-space no --preserve-entities yes --drop-empty-elements no --drop-empty-paras no --show-body-only yes --warn-proprietary-attributes no --wrap-php no --keep-tabs yes -ashtml -m $@ ; test $$? -lt 2 && true ; ./unwrap-php.sh $@ ) ) || ( rm -f $@ ; false )
+
+SCMFILES = $(wildcard *.scm)
+SCMFILES := $(filter-out $(wildcard common.scm php.scm main.scm html4*.scm lang-tags.scm form.scm spinkit.scm speakhtml*.scm),$(SCMFILES))
+PHPFILES = $(SCMFILES:.scm=.php)
+
+all: php
+
+rebuild: clean all
+
+php: $(PHPFILES)
+
+phpclean:
+ rm -f $(PHPFILES)
+
+clean: phpclean
diff --git a/addcomm.php b/addcomm.php
new file mode 100644
index 0000000..6dbc29a
--- /dev/null
+++ b/addcomm.php
@@ -0,0 +1,32 @@
+<?php
+/*
+ * 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.
+ */
+?>
+<?php
+/* vim: set ts=4 sw=4 et : */
+
+require_once __DIR__ . "/check_auth.php";
+require_once __DIR__ . "/../includes/common.php";
+
+if (!isset($_POST['persid']))
+ exit(0);
+
+$sql = "INSERT INTO commissioners (Person) VALUES (?);";
+$stmt = $mysqli->prepare($sql);
+$stmt->bind_param('i', $_POST['persid']);
+$stmt->execute();
+$stmt->reset();
+$mysqli->close();
+
+echo "1";
+
+exit(0);
diff --git a/addtemplate.php b/addtemplate.php
new file mode 100644
index 0000000..29bc05f
--- /dev/null
+++ b/addtemplate.php
@@ -0,0 +1,42 @@
+<?php
+/*
+ * 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.
+ */
+?>
+<?php
+/* vim: set ts=4 sw=4 et : */
+
+require_once __DIR__ . "/check_auth.php";
+require_once __DIR__ . "/../includes/common.php";
+
+if (!isset($_POST['name']) || !isset($_POST['text']))
+ exit(0);
+
+if (isset($_POST['email'])) {
+ $sql = "INSERT INTO email_templates (name, text, `from`, cc, bcc, subject) VALUES (?, ?, ?, ?, ?, ?);";
+
+ $stmt = $mysqli->prepare($sql);
+ $stmt->bind_param('ssssss', $_POST['name'], $_POST['text'], $_POST['from'], $_POST['cc'], $_POST['bcc'], $_POST['subject']);
+ $stmt->execute();
+} else if (isset($_POST['pdf'])) {
+ $sql = "INSERT INTO pdf_templates (name, text, subject, date) VALUES (?, ?, ?, ?);";
+
+ $stmt = $mysqli->prepare($sql);
+ $stmt->bind_param('ssss', $_POST['name'], $_POST['text'], $_POST['subject'], $_POST['date']);
+ $stmt->execute();
+}
+
+echo $stmt->insert_id;
+
+$stmt->reset();
+$mysqli->close();
+
+exit(0);
diff --git a/allocation.php b/allocation.php
new file mode 100644
index 0000000..e6fd038
--- /dev/null
+++ b/allocation.php
@@ -0,0 +1,59 @@
+<?php
+/*
+ * 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.
+ */
+?>
+<?php
+/* vim: set ts=4 sw=4 et : */
+
+$year = 2022;
+if (isset($_GET['year'])) $year = $_GET['year'];
+$_title = "Mittelverwendung ($year)";
+require_once __DIR__ . "/check_auth.php";
+require_once __DIR__ . "/../includes/common.php";
+require_once __DIR__ . "/header.php";
+?>
+
+<h1><?=$_title?></h1>
+
+<?
+(function () use ($mysqli, $constraint, $nospinner, $noactions, $year) {
+ if (!isset($noactions))
+ $noactions = false;
+
+ $sql = "
+SELECT contracts.id AS VertrID, contracts.`call`, Förderer.ID AS FoerdID, Organisationen.Name AS Förderer, Personen.Nachname as Zuständig, contracts.ls, contracts.ls_months AS` LS-Monate`, contracts.ss, contracts.ss_months AS `SS-Monate`, (contracts.ls_months + contracts.ss_months) * 150 AS `∑ projektiert`,
+SUM( TIMESTAMPDIFF(MONTH, Stipendien.Förderbeginn, Stipendien.Förderende) + 1 ) AS `Monate allokiert`,
+SUM( TIMESTAMPDIFF(MONTH, Stipendien.Förderbeginn, Stipendien.Förderende) + 1) * 150 AS `∑ allokiert`,
+((contracts.ls_months + contracts.ss_months) * 150) - (SUM( TIMESTAMPDIFF(MONTH, Stipendien.Förderbeginn, Stipendien.Förderende) + 1) * 150) AS `Delta`
+FROM contracts
+INNER JOIN Förderer ON contracts.patron = Förderer.ID
+LEFT JOIN Organisationen ON Förderer.Organisation = Organisationen.ID
+LEFT JOIN Personen ON Förderer.zuständig = Personen.ID
+LEFT JOIN Stipendien ON Stipendien.Förderer = Förderer.ID AND Stipendien.Jahr = 2021 AND Stipendien.Förderart NOT IN (2, 5)
+WHERE contracts.`call`=?
+GROUP BY contracts.id
+";
+ $sql .= $constraint;
+ $stmt = $mysqli->prepare($sql);
+ $stmt->bind_param("i", $year);
+ $stmt->execute();
+ $r = $stmt->get_result();
+ $id = "Mittelverwendung";
+ $order = '[[1, "asc"], [3, "asc"], [4, "asc"]]';
+ $ajax = false;
+ $entrytable = 'Mittelverwendung';
+ $nospinner = true;
+ include __DIR__ . '/autotable.php';
+ $stmt->reset();
+})();
+
+include_once __DIR__ . "/footer.php";
diff --git a/alumni.pers.php b/alumni.pers.php
new file mode 100644
index 0000000..2183203
--- /dev/null
+++ b/alumni.pers.php
@@ -0,0 +1,64 @@
+<?php
+/*
+ * 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.
+ */
+?>
+<?php
+/* vim: set ts=4 sw=4 et : */
+
+$_title = "Alle Alumni (nur Person; anhand Stipendientabelle)";
+
+require_once __DIR__ . "/check_auth.php";
+include_once __DIR__ . "/header.php";
+
+require_once __DIR__ . "/../includes/common.php";
+require_once __DIR__ . "/lookup.php";
+
+doTitle();
+
+(function () {
+ $sql = "
+SELECT
+ Personen.ID AS PersID,
+ Personen.Nachname, Personen.Vorname,
+ Personen.Geschlecht,
+ Personen.Ort,
+ Personen.Email,
+ Personen.Telefon,
+ Personen.Handy,
+ Personen.wuenscht_keine_emails,
+ Personen.ideellesfp,
+ Personen.stellenangebote,
+ Personen.infos,
+ Personen.newsletter
+
+FROM Stipendien
+LEFT JOIN Personen ON Stipendien.Person = Personen.ID
+WHERE Stipendien.Förderende < NOW() AND Stipendien.Förderende > '2000-01-01 00:00:00' AND Personen.ID NOT IN (SELECT Person FROM Stipendien WHERE Stipendien.Förderende >= NOW())
+GROUP BY Personen.ID
+";
+ $id = "alumnipers";
+ $getthdef = true;
+ $order = '[[2, "asc"], [3, "asc"]]';
+ $checkboxes = true;
+ $idcell = "PersID";
+ $ajax = true;
+ $nospinner = true;
+ include __DIR__ . '/autotable.php';
+
+ $pdf_sql = $email_sql = "SELECT *, ID AS PersID FROM Personen WHERE ID IN ";
+ include __DIR__ . '/autoactions.php';
+ include __DIR__ . '/autoemail.php';
+ include __DIR__ . '/autopdf.php';
+ include __DIR__ . '/autoevent.php';
+})();
+
+include_once __DIR__ . "/footer.php";
diff --git a/alumni.php b/alumni.php
new file mode 100644
index 0000000..a42c6da
--- /dev/null
+++ b/alumni.php
@@ -0,0 +1,19 @@
+<?php
+/*
+ * 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.
+ */
+?>
+<?php
+/* vim: set ts=4 sw=4 et : */
+
+$_title = "Alumni (mit Stipendien; anhand Stipendientabelle)";
+$_constraint = "WHERE Stipendien.Förderende < NOW() AND Stipendien.Förderende > '2000-01-01 00:00:00' AND Personen.ID NOT IN (SELECT Person FROM Stipendien WHERE Stipendien.Förderende >= NOW())";
+require_once __DIR__ . '/stips_common.php';
diff --git a/auto_common.php b/auto_common.php
new file mode 100644
index 0000000..9b74adb
--- /dev/null
+++ b/auto_common.php
@@ -0,0 +1,180 @@
+<?php
+/*
+ * 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.
+ */
+?>
+<?
+/* vim: set ts=4 sw=4 et : */
+
+function getPii($pii = []) {
+ $pii_default = ['Vorname', 'Nachname', 'Name', 'name', 'Förderer', 'Organisation', 'Ort', 'Email', 'Email-Privat', 'Email-Uni', 'Email-Geschäftlich', 'Telefon', 'Handy', 'Kategorie', 'Event', 'Geburtsname', 'Geburtsort', 'PLZ', 'Straße', 'Adresszusatz', 'place', 'teaser', 'description', 'notes', 'host', 'category', 'Kontoinhaber', 'Bankname', 'IBAN', 'BIC', 'Ansprechpartner Stipendiaten', 'Zuständig'];
+ if (!is_array($pii))
+ return $pii_default;
+ return array_merge($pii_default, $pii);
+}
+
+function getLinks($links = []) {
+ $links_default = [
+ 'PersID' => '/db/person/',
+ 'APID' => '/db/person/',
+ 'APZuwID' => '/db/person/',
+ 'ZustID' => '/db/person/',
+ 'StipID' => '/db/stip/',
+ 'FoerdID' => '/db/patron/',
+ 'VorhFoerdID' => '/db/patron/',
+ 'OrgaID' => '/db/orga/',
+ 'SuperOrgaID' => '/db/orga/',
+ 'SpendenID' => '/db/donation/',
+ 'SuperSpendenID' => '/db/donation/',
+ 'EventID' => '/db/event/',
+ 'SuperEventID' => '/db/event/',
+ 'EventCatID' => '/db/event/category/',
+ 'NotizID' => '/db/note/',
+ 'EmailID' => '/db/email/',
+ 'VertrID' => '/db/contract/',
+ 'KanbanID' => '/db/kanban/',
+ 'KanbanViewID' => '/db/kanban/view/',
+ 'KanbanBoardID' => '/db/kanban/board/',
+ 'ETID' => '/db/templates/email/',
+ 'PTID' => '/db/templates/pdf/',
+ 'AppNewsID' => '/db/main/news.php?id=',
+ 'AppSettingID' => '/db/main/appsetting.php?id=',
+ 'AppProfileID' => '/db/main/appprofile.php?id=',
+ 'AppCampaignID' => '/db/main/appcampaign.php?id=',
+ 'AppBillboardID' => '/db/main/appbillboard.php?id=',
+ 'AppNagscreenID' => '/db/main/appnagscreen.php?id=',
+ ];
+ if (!is_array($links))
+ return $links_default;
+ return array_merge($links_default, $links);
+}
+
+function ajaxLinks() {
+?>
+ {
+ render: function (data, type, row, meta) {
+ if (data === "" || data == null) return data;
+ return '<a class="autolink" href="/db/person/' + data + '">' + data + '</a>';
+ }, targets: ['at_col_persid', 'at_col_PersID', 'at_col_APID', 'at_col_APZuwID', 'at_col_ZustID']
+ },
+ {
+ render: function (data, type, row, meta) {
+ if (data === "" || data == null) return data;
+ return '<a class="autolink" href="/db/stip/' + data + '">' + data + '</a>';
+ }, targets: 'at_col_StipID'
+ },
+ {
+ render: function (data, type, row, meta) {
+ if (data === "" || data == null) return data;
+ return '<a class="autolink" href="/db/patron/' + data + '">' + data + '</a>';
+ }, targets: ['at_col_FoerdID', 'at_col_VorhFoerdID']
+ },
+ {
+ render: function (data, type, row, meta) {
+ if (data === "" || data == null) return data;
+ return '<a class="autolink" href="/db/orga/' + data + '">' + data + '</a>';
+ }, targets: ['at_col_OrgaID', 'at_col_SuperOrgaID']
+ },
+ {
+ render: function (data, type, row, meta) {
+ if (data === "" || data == null) return data;
+ return '<a class="autolink" href="/db/note/' + data + '">' + data + '</a>';
+ }, targets: 'at_col_NotizID'
+ },
+ {
+ render: function (data, type, row, meta) {
+ if (data === "" || data == null) return data;
+ return '<a class="autolink" href="/db/event/' + data + '">' + data + '</a>';
+ }, targets: ['at_col_EventID', 'at_col_SuperEventID']
+ },
+ {
+ render: function (data, type, row, meta) {
+ if (data === "" || data == null) return data;
+ return '<a class="autolink" href="/db/event/category/' + data + '">' + data + '</a>';
+ }, targets: 'at_col_EventCatID'
+ },
+ {
+ render: function (data, type, row, meta) {
+ if (data === "" || data == null) return data;
+ return '<a class="autolink" href="/db/email/' + data + '">' + data + '</a>';
+ }, targets: 'at_col_EmailID'
+ },
+ {
+ render: function (data, type, row, meta) {
+ if (data === "" || data == null) return data;
+ return '<a class="autolink" href="/db/donation/' + data + '">' + data + '</a>';
+ }, targets: ['at_col_SpendenID', 'at_col_SuperSpendenID']
+ },
+ {
+ render: function (data, type, row, meta) {
+ if (data === "" || data == null) return data;
+ return '<a class="autolink" href="/db/contract/' + data + '">' + data + '</a>';
+ }, targets: 'at_col_VertrID'
+ },
+ {
+ render: function (data, type, row, meta) {
+ if (data === "" || data == null) return data;
+ return '<a class="autolink" href="/db/kanban/' + data + '">' + data + '</a>';
+ }, targets: 'at_col_KanbanID'
+ },
+ {
+ render: function (data, type, row, meta) {
+ if (data === "" || data == null) return data;
+ return '<a class="autolink" href="/db/kanban/board/' + data + '">' + data + '</a>';
+ }, targets: 'at_col_KanbanBoardID'
+ },
+ {
+ render: function (data, type, row, meta) {
+ if (data === "" || data == null) return data;
+ return '<a class="autolink" href="/db/kanban/view/' + data + '">' + data + '</a>';
+ }, targets: 'at_col_KanbanViewID'
+ },
+ {
+ render: function (data, type, row, meta) {
+ if (data === "" || data == null) return data;
+ return '<a class="autolink" href="/db/main/news.php?id=' + data + '">' + data + '</a>';
+ }, targets: 'at_col_AppNewsID'
+ },
+ {
+ render: function (data, type, row, meta) {
+ if (data === "" || data == null) return data;
+ return '<a class="autolink" href="/db/main/appsetting.php?id=' + data + '">' + data + '</a>';
+ }, targets: 'at_col_AppSettingID'
+ },
+ {
+ render: function (data, type, row, meta) {
+ if (data === "" || data == null) return data;
+ return '<a class="autolink" href="/db/main/appprofile.php?id=' + data + '">' + data + '</a>';
+ }, targets: 'at_col_AppProfileID'
+ },
+ {
+ render: function (data, type, row, meta) {
+ if (data === "" || data == null) return data;
+ return '<a class="autolink" href="/db/main/appcampaign.php?id=' + data + '">' + data + '</a>';
+ }, targets: 'at_col_AppCampaignID'
+ },
+ {
+ render: function (data, type, row, meta) {
+ if (data === "" || data == null) return data;
+ return '<a class="autolink" href="/db/main/appbillboard.php?id=' + data + '">' + data + '</a>';
+ }, targets: 'at_col_AppBillboardID'
+ },
+ {
+ render: function (data, type, row, meta) {
+ if (data === "" || data == null) return data;
+ return '<a class="autolink" href="/db/main/appnagscreen.php?id=' + data + '">' + data + '</a>';
+ }, targets: 'at_col_AppNagscreenID'
+ },
+
+
+<?
+}
+?>
diff --git a/autoactions.php b/autoactions.php
new file mode 100644
index 0000000..d37e18c
--- /dev/null
+++ b/autoactions.php
@@ -0,0 +1,48 @@
+<?php
+/*
+ * 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.
+ */
+?>
+<?
+
+(function () use ($id) {
+?>
+
+<div id="<?=$id?>Actions" class="admin_actionsbelow">
+ Ausgewählte
+</div>
+
+<div id="<?=$id?>ActionsOuter" style="display: inline-block;">
+</div>
+
+<script>
+<? minStart(); ?>
+
+(function () {
+ var a = $('#<?=$id?>Actions');
+ var b = $('#<?=$id?>ActionsOuter');
+ a.detach();
+ b.detach();
+
+ if ($('#<?=$id?>TableInner').length > 0) {
+ a.appendTo('#<?=$id?>TableInner');
+ b.appendTo('#<?=$id?>TableInner');
+ } else {
+ a.appendTo('#<?=$id?>Table');
+ b.appendTo('#<?=$id?>Table');
+ }
+})();
+
+<? minEnd(); ?>
+</script>
+<?
+})();
+?>
diff --git a/autoeemail.php b/autoeemail.php
new file mode 100644
index 0000000..aa1450c
--- /dev/null
+++ b/autoeemail.php
@@ -0,0 +1,31 @@
+<?php
+/*
+ * 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.
+ */
+?>
+<div style="display: none;">
+<form id="doemail" action="/db/main/e.php" method="post">
+ <input id="doemail_ids" name="ids" type="hidden" value="">
+</form>
+</div>
+
+<script>
+<? minStart(); ?>
+ function doEmail() {
+ var ids = [];
+ $('table tr.selected').each(function () {
+ ids.push($(this).attr('data-id'));
+ });
+ $('#doemail_ids').val(ids);
+ $('#doemail').submit();
+ }
+<? minEnd(); ?>
+</script>
diff --git a/autoemail.php b/autoemail.php
new file mode 100644
index 0000000..cac5647
--- /dev/null
+++ b/autoemail.php
@@ -0,0 +1,80 @@
+<?php
+/*
+ * 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.
+ */
+?>
+<?
+
+(function () use ($id, $email_sql, $payload_sql, $independent_payload_sql, $payload_sql_order, $independent_payload_sql_order, $nocount, $post_sql, $exactmatch) {
+ if (!isset($nocount)) $nocount = false;
+ if (!isset($payload_sql)) $payload_sql = "";
+ if (!isset($payload_sql_order)) $payload_sql_order = "";
+ if (!isset($independent_payload_sql)) $independent_payload_sql = "";
+ if (!isset($independent_payload_sql_order)) $independent_payload_sql_order = "";
+ if (!isset($exactmatch)) $exactmatch = 0;
+?>
+
+<div style="display: none;">
+ <form id="doemail_<?=$id?>" action="/db/main/email.php" method="post">
+ <input id="doemail_ids_<?=$id?>" name="ids" type="hidden" value="">
+ <input id="doemail_sql_<?=$id?>" name="sql" type="hidden" value="<?=$email_sql?>">
+ <input id="doemail_postsql_<?=$id?>" name="postsql" type="hidden" value="<?=$post_sql?>">
+ <input id="doemail_payload_sql_<?=$id?>" name="payload_sql" type="hidden" value="<?=$payload_sql?>">
+ <input id="doemail_payload_sql_order_<?=$id?>" name="payload_sql_order" type="hidden" value="<?=$payload_sql_order?>">
+ <input id="doemail_independent_payload_sql_<?=$id?>" name="independent_payload_sql" type="hidden" value="<?=$independent_payload_sql?>">
+ <input id="doemail_independent_payload_sql_order_<?=$id?>" name="independent_payload_sql_order" type="hidden" value="<?=$independent_payload_sql_order?>">
+ <input id="doemail_nocount_<?=$id?>" name="nocount" type="hidden" value="<?=$nocount?>">
+ <input id="doemail_exactmatch<?=$id?>" name="exactmatch" type="hidden" value="<?=$exactmatch?>">
+ </form>
+</div>
+
+<script>
+<?php minStart(); ?>
+
+function doEmail_<?=$id?>(ids) {
+ if (typeof ids === 'undefined') {
+ if (typeof autotables === 'undefined') {
+ ids = [];
+ $('#<?=$id?> tr.selected').each(function () {
+ ids.push($(this).attr('data-id'));
+ });
+ } else {
+ var g = $.grep(autotables['<?=$id?>'].table().rows().nodes(), function (e) {
+ return e.classList.contains('selected');
+ });
+ ids = [];
+ $.each(g, function (i, e) {
+ var ee = false;
+ var eee = false;
+ try { ee = e.getElementsByClassName('ajaxident')[0].getAttribute('data-id'); } catch (e) { e; }
+ try { eee = e.getElementsByClassName('ajaxident')[0].getAttribute('data-persid'); } catch (e) { e; }
+ if (ee) ids.push(ee);
+ else if (eee) ids.push(eee);
+ else if (e.getAttribute('data-id')) ids.push(e.getAttribute('data-id'));
+ else ids.push(e.getAttribute('data-persid'));
+ });
+ }
+ }
+ $('#doemail_ids_<?=$id?>').val(ids);
+ $('#doemail_<?=$id?>').submit();
+}
+
+if ($('#<?=$id?>Actions').length > 0) {
+ $('#<?=$id?>Actions').append('&nbsp;');
+ $('<button class="medium" onclick="return doEmail_<?=$id?>();"><i class="fas fa-envelope"></i> E-Mail senden</button>').appendTo('#<?=$id?>Actions');
+}
+
+<?php minEnd(); ?>
+</script>
+
+<?
+})();
+?>
diff --git a/autoform.php b/autoform.php
new file mode 100644
index 0000000..21f9caa
--- /dev/null
+++ b/autoform.php
@@ -0,0 +1,646 @@
+<?php
+/*
+ * 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.
+ */
+?>
+<?
+/* vim: set ts=4 sw=4 et : */
+
+require_once __DIR__ . "/lookup_more.php";
+require_once __DIR__ . "/auto_common.php";
+
+(function () use ($id, $simple, $def, $links, $l, $entrytable, $pii, $bottom, $updater) {
+ $pii = getPii($pii);
+ $links = getLinks($links);
+ if (!isset($updater) || $updater == "")
+ $updater = '/db/main/update.php';
+?>
+
+<span class="prevent" style="margin 0; padding: 0; vertical-align: bottom;">
+ <i style="cursor: pointer;" onclick="toggleForm('<?=$id?>', 0);" id="<?=$id?>Form_off" class="fas fa-toggle-on"></i>
+ <i style="cursor: pointer; display: none;" onclick="toggleForm('<?=$id?>', 1);" id="<?=$id?>Form_on" class="fas fa-toggle-off"></i>
+</span>
+
+<div style="padding: .15em; margin: 0;"></div>
+
+<script>
+<? minStart(); ?>
+ function toggleForm(id, state) {
+ if (state == 1) {
+ $('#' + id + 'Form').show();
+ $('#' + id + 'Form_on').hide();
+ $('#' + id + 'Form_off').show();
+ setTimeout(function () {
+ $(window).resize();
+ }, 10);
+ return;
+ }
+ $('#' + id + 'Form').hide();
+ $('#' + id + 'Form_on').show();
+ $('#' + id + 'Form_off').hide();
+ setTimeout(function () {
+ $(window).resize();
+ }, 10);
+ return;
+ }
+<? minEnd(); ?>
+</script>
+
+<form id="<?=$id?>Form" class="grid-form" style="background: silver; max-width: 1200px;" autocomplete="off">
+
+<fieldset id="<?=$id?>App" ng-controller="<?=$id?>Ctrl" style="display: none;">
+
+<?
+ if ($simple) {
+ foreach ($l as $k => $v) {
+?>
+ <div data-row-span="1">
+ <div data-field-span="1" class="editable">
+ <label><?=$k?></label>
+ <input type="text" name="<?=$k?>" value="<?=str_replace('"', '&quot;', $v)?>">
+ </div>
+ </div>
+<?
+ }
+ }
+?>
+
+<?
+ foreach ($def as $line) {
+ if (is_string($line)) {
+?>
+ <legend><?=$line?></legend>
+<?
+ } else {
+?>
+ <div data-row-span="<? if (is_numeric($line[0])) { echo $line[0]; array_shift($line); } else { echo count($line); } ?>">
+<?
+ foreach ($line as $e) {
+ $label = "";
+ $e_split = explode('~', $e);
+ $e = $e_split[0];
+ if (count($e_split) > 1)
+ $label = $e_split[1];
+
+ $dis = false;
+ if ($e[0] === '_') {
+ $e = substr($e, 1);
+ $dis = true;
+ }
+
+ $verbatim = false;
+ if ($e[0] === '!') {
+ $e = substr($e, 1);
+ $verbatim = true;
+ }
+
+ $code = false;
+ if ($e[0] === '{') {
+ $e = substr($e, 1);
+ $code = true;
+ }
+
+ $html = false;
+ if ($e[0] === '[') {
+ $e = substr($e, 1);
+ $html = true;
+ }
+
+ $emailhtml = false;
+ if ($e[0] === '@') {
+ $e = substr($e, 1);
+ $emailhtml = true;
+ }
+
+ $quill = false;
+ if ($e[0] === '*') {
+ $e = substr($e, 1);
+ $quill = true;
+ }
+
+ $ace = false;
+ if ($e[0] === '}') {
+ $e = substr($e, 1);
+ $ace = true;
+ }
+
+ $boolean = false;
+ if ($e[0] === '+') {
+ $e = substr($e, 1);
+ $boolean = true;
+ }
+
+ $btn = false;
+ if ($e[0] === '^') {
+ $e = substr($e, 1);
+ $btn = true;
+ }
+
+ $cpick = false;
+ if ($e[0] === '"') {
+ $e = substr($e, 1);
+ $cpick = true;
+ }
+
+ $udecode = false;
+ if ($e[0] === '%') {
+ $e = substr($e, 1);
+ $udecode = true;
+ }
+
+ $e_split = explode('|', $e);
+ $e = $e_split[0];
+
+ $lookup = false;
+ if (count($e_split) > 1)
+ $lookup = $e_split[1];
+
+ $class = "";
+ $e_split = explode('#', $e);
+ $e = $e_split[0];
+ if (count($e_split) > 1)
+ $class = $e_split[1];
+?>
+<?
+ if (substr($e, 0, 1) === '=') {
+?>
+ <div data-field-span="<? echo substr($e, 1, 1); $e = substr($e, 2); ?>" class="<? if (!$dis) { echo "editable"; } else { echo "disabled"; }?> <?=$class?>">
+<?
+ } else {
+?>
+ <div data-field-span="1" class="<? if (!$dis) { echo "editable"; } else { echo "disabled"; }?> <?=$class?>">
+<?
+ }
+ if ($udecode) {
+ //$l->$e = html_entity_decode(preg_replace("/%u([0-9a-f]{3,4})/i","&#x\\1;", urldecode($l->$e)), null, 'UTF-8');
+ $l->$e = utf8_encode(urldecode($l->$e));
+ }
+?>
+<?
+ if ($e !== '=') {
+ if ($btn) {
+?>
+ <label></label>
+<?
+ } else {
+?>
+ <label title="<?=$e?>"><? if ($label) { echo $label; } else { echo $e; } ?></label>
+<?
+ }
+ }
+?>
+<?
+ if (isset($links[$e])) {
+?>
+ <? if ($l->$e != 0) { ?><a class="autolink" href="<?=$links[$e]?><?=$l->$e?>" style="color: blue !important; font-weight: bold;"><?=$l->$e?></a><? } ?>
+<?
+ } else if (isset($links[$label])) {
+?>
+ <? if ($l->$e != 0) { ?><a class="autolink" href="<?=$links[$label]?><?=$l->$e?>" style="color: blue !important; font-weight: bold;"><?=$l->$e?></a><? } ?>
+<?
+ } else {
+ if (in_array($e, $pii))
+ $class .= ' pii';
+
+ if ($lookup) {
+ global $$lookup;
+?>
+ <select class="<?=$class?>" name="<?=$e?>">
+ <option value="null"></option>
+<?
+
+ $_found = false;
+ foreach ($$lookup as $k => $v) {
+?>
+ <option value="<?=str_replace('"', '&quot;', $k)?>" <? if ($l->$e == $k) { $_found = true; echo "selected"; } ?>><?=$v?></option>
+<?
+ }
+ if (!$_found && $l->$e !== null) {
+?>
+ <option value="invalid" disabled selected style="color: red!important;">[Unzulässiger Wert!] {<?=$l->$e?>}</option>
+<?
+ }
+?>
+ </select>
+<?
+ } else if ($boolean) {
+?>
+ <input class="<?=$class?>" name="<?=$e?>" type="checkbox" <? if ($l->$e) { echo "checked"; } ?>>
+<?
+ } else if ($e === '=') {
+?>
+<?
+ } else if ($verbatim) {
+?>
+ <?=$l->$e?>
+<?
+ } else if ($code) {
+?>
+ <pre ng-non-bindable style="display: inline-block; background: #eee; color: black; margin: 0; padding: .2em; white-space: pre-wrap; word-wrap: break-word; overflow-wrap: break-word;"><?=$l->$e?></pre>
+<?
+ } else if ($html) {
+?>
+ <iframe style="background: #eee; color: black; margin: 0; padding: .2em; border: 0; width: 100%; height: 100%;"
+ onload="this.style.height = this.contentWindow.document.documentElement.scrollHeight + 15 + 'px'; $(this).parent().resize();"
+ csrc="/db/main/reflector.php?foo=<?=urlencode($l->$e)?>"></iframe>
+<?
+ } else if ($emailhtml) {
+?>
+ <iframe style="background: #eee; color: black; margin: 0; padding: .2em; border: 0; width: 100%; height: 100%;"
+ onload="this.style.height = this.contentWindow.document.documentElement.scrollHeight + 15 + 'px'; $(this).parent().resize();"
+ src="/db/main/reflector.php?emailid=<?=$l->$e?>"></iframe>
+<?
+ } else if ($quill) {
+?>
+ <div ng-non-bindable class="editorcontainer" style="background: white; color: black;">
+ <div class="editor <? if (in_array($e, $pii)) { echo "pii"; } ?>" name="<?=$e?>" id="editor_<?=$e?>"><?=str_replace("\r", " ", str_replace("\n", " ", trim($l->$e)))?></div>
+ </div>
+<?
+ } else if ($ace) {
+?>
+ <div ng-non-bindable class="editorcontainer ace" style="background: white; color: black;">
+ <div class="ace <? if (in_array($e, $pii)) { echo "pii"; } ?>" style="resize:vertical; overflow:auto; min-height: 16em;" name="<?=$e?>" id="ace_<?=$e?>"><?=str_replace(" ", " ", str_replace("\t", ' ', str_replace('&nbsp;', ' ', str_replace('<br>', "\n", str_replace('</p>', " ", str_replace('<p>', "\n", trim($l->$e)))))))?></div>
+ </div>
+<?
+ } else if ($btn) {
+?>
+ <button class="formbtn btn_<?=$e?>"><?=$label?></button>
+<?
+ } else if ($cpick) {
+?>
+ <input class="<?=$class?>" type="color" name="<?=$e?>" value="<?=$l->$e?>">
+<?
+ } else {
+?>
+ <input class="<?=$class?>" type="text" name="<?=$e?>" value="<?=str_replace('"', '&quot;', $l->$e)?>">
+<?
+ }
+ }
+?>
+ </div>
+<?
+ }
+?>
+ </div>
+<?
+ }
+ }
+?>
+</fieldset>
+
+<?=$bottom?>
+
+<button style="display: none;" class="savebutton" onclick="return writeChanges();"><i class="fas fa-save"></i> Speichern</button>
+<button style="display: none; position: fixed; top: 2em; left: min(1150px, calc(100vw - 2.5em)); opacity: .8;" class="savebutton floating" onclick="return writeChanges();"><i class="fas fa-save"></i></button>
+</form>
+
+<script>
+<? minStart(); ?>
+ $('.prevent').on('mousedown', function (e) {
+ var doc = $(document);
+ e.preventDefault();
+ var handler = function () {
+ e.preventDefault();
+ };
+ doc.on('mousemove', handler);
+ doc.one('mouseup', function (e) {
+ doc.off('mousemove', handler);
+ });
+ });
+
+ var Delta = Quill.import('delta');
+ var Break = Quill.import('blots/break');
+ var Embed = Quill.import('blots/embed');
+
+ function lineBreakMatcher() {
+ var newDelta = new Delta();
+ newDelta.insert({'break': ''});
+ return newDelta;
+ }
+
+ class SmartBreak extends Break {
+ length () {
+ return 1;
+ }
+ value () {
+ return '\n';
+ }
+ insertInto(parent, ref) {
+ Embed.prototype.insertInto.call(this, parent, ref);
+ }
+ }
+ SmartBreak.blotName = 'break';
+ SmartBreak.tagName = 'BR'
+ Quill.register(SmartBreak);
+
+ var editors = [];
+ $('.editorcontainer .editor').each(function () {
+ var that = this;
+ var quill = new Quill(this, {
+ theme: 'snow',
+ modules: {
+ toolbar: [
+ [{ header: [1, 2, 3, false] }],
+ [{'size': ['small', false, 'large', 'huge']}],
+ ['bold', 'italic', 'underline', 'strike'],
+ [{ 'color': [] }, { 'background': [] }],
+ ['link', 'blockquote', 'image'],
+ [{'list': 'ordered'}, {'list': 'bullet'}],
+ [{'script': 'sub'}, {'script': 'super'}],
+ [{'indent': '-1'}, {'indent': '+1'}],
+ [{'align': []}],
+ ['clean']
+ ],
+ clipboard: {
+ matchers: [
+ ['BR', lineBreakMatcher]
+ ]
+ },
+ keyboard: {
+ bindings: {
+ linebreak: {
+ key: 13,
+ shiftKey: true,
+ handler: function (range) {
+ var currentLeaf = this.quill.getLeaf(range.index)[0];
+ var nextLeaf = this.quill.getLeaf(range.index + 1)[0];
+
+ this.quill.insertEmbed(range.index, 'break', true, 'user');
+
+ if (nextLeaf === null || (currentLeaf.parent !== nextLeaf.parent)) {
+ this.quill.insertEmbed(range.index, 'break', true, 'user');
+ }
+
+ this.quill.setSelection(range.index + 1, Quill.sources.SILENT);
+ }
+ }
+ }
+ }
+ }
+ });
+ //that.firstChild.innerHTML = that.firstChild.innerHTML.trim().replace(/<p><br><\/p>$/, "").replace(/<br>/g, "\n");
+ setTimeout(function () {
+ quill.on('text-change', function () {
+ that.firstChild.classList.add('changed');
+ $('#<?=$id?>Form .savebutton').show();
+ $('#<?=$id?>Form').trigger('resize');
+ });
+ }, 100);
+ editors.push(quill);
+ });
+
+ var aceeditors = [];
+ $('.editorcontainer .ace').each(function () {
+ var that = this;
+ var editor = ace.edit(this);
+ editor.session.setMode("ace/mode/match");
+ editor.setTheme("ace/theme/monokai");
+ editor.setAutoScrollEditorIntoView(true);
+ editor.setShowPrintMargin(false);
+ editor.setShowInvisibles(false);
+ editor.setOption('highlightActiveLine', true);
+ editor.getSession().setTabSize(4);
+ editor.getSession().setUseSoftTabs(true);
+ editor.getSession().setUseWrapMode(true);
+
+ setTimeout(function () {
+ editor.on('change', function () {
+ that.classList.add('changed');
+ $('#<?=$id?>Form .savebutton').show();
+ $('#<?=$id?>Form').trigger('resize');
+ return false;
+ });
+ }, 100);
+
+ var lastHeight = 0;
+ setInterval(function () {
+ if (lastHeight != that.clientHeight) {
+ editor.resize();
+ if (that.clientHeight < lastHeight) {
+ editor.renderer.updateFull();
+ }
+ }
+ lastHeight = that.clientHeight;
+ }, 100);
+
+ $(window).on('resize', function() {
+ editor.resize();
+ editor.renderer.updateFull();
+ });
+
+ aceeditors.push(editor);
+ });
+
+// $(document).ready(function () {
+ setTimeout(function() {
+ var app = angular.module('<?=$id?>App', []);
+ app.controller('<?=$id?>Ctrl', ['$scope', function ($scope) {
+ }]);
+ angular.bootstrap($('#<?=$id?>App'), ['<?=$id?>App']);
+ $('#<?=$id?>App').show();
+
+<? if ($_SESSION['scramble']) { ?>
+ $('#<?=$id?>App input.pii').each(function (i, e) {
+ e.value = nrot3(nshuffle(e.value));
+ });
+ $('#<?=$id?>App select.pii option').each(function (i, e) {
+ e.innerHTML = nrot3(nshuffle(e.innerHTML));
+ });
+ $('#<?=$id?>App .editor.pii').each(function (i, e) {
+ e.innerHTML = nrot3(nshuffle(e.innerHTML));
+ });
+<? } ?>
+ $('#<?=$id?>Form .savebutton').hide();
+
+ $('.disabled input, .disabled select').prop('disabled', true);
+
+ $('.editable input, .editable select:not(.ql-background, .ql-color, .ql-header, .ql-size, .ql-picker .ql-formats)').on('change input cut paste', function () {
+ if ($(this).parent().hasClass('ql-formats') || $(this).parent().hasClass('ql-toolbar'))
+ return;
+ if ($(this).parent().hasClass('ql-tooltip'))
+ return;
+
+ $(this).parent().addClass('changed');
+ $('#<?=$id?>Form .savebutton').show();
+ });
+
+ $('.editable.date input, .editable.isodate input').datepicker();
+ /*
+ flatpickr({
+ weekNumbers: true,
+ shorthandCurrentMonth: true,
+ locale: {
+ firstDayOfWeek: 1,
+ weekdays: {
+ shorthand: ['So', 'Mo', 'Di', 'Mi', 'Do', 'Fr', 'Sa'],
+ longhand: ['Sonntag', 'Montag', 'Dienstag', 'Mittwoch', 'Donnerstag', 'Freitag', 'Samstag'],
+ },
+ months: {
+ shorthand: ['Jan', 'Feb', 'Mär', 'Apr', 'Mai', 'Jun', 'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Dez'],
+ longhand: ['Januar', 'Februar', 'März', 'April', 'Mai', 'Juni', 'Juli', 'August', 'September', 'Oktober', 'November', 'Dezember']
+ },
+ },
+ });
+ */
+
+ $('.editable.isodatetime input').datepicker();
+ /*flatpickr({
+ enableTime: true,
+ time_24hr: true,
+ weekNumbers: true,
+ shorthandCurrentMonth: true,
+ locale: {
+ firstDayOfWeek: 1,
+ weekdays: {
+ shorthand: ['So', 'Mo', 'Di', 'Mi', 'Do', 'Fr', 'Sa'],
+ longhand: ['Sonntag', 'Montag', 'Dienstag', 'Mittwoch', 'Donnerstag', 'Freitag', 'Samstag'],
+ },
+ months: {
+ shorthand: ['Jan', 'Feb', 'Mär', 'Apr', 'Mai', 'Jun', 'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Dez'],
+ longhand: ['Januar', 'Februar', 'März', 'April', 'Mai', 'Juni', 'Juli', 'August', 'September', 'Oktober', 'November', 'Dezember']
+ };
+ },
+ });
+ */
+
+<?
+ (function () use ($id) {
+ $sel_id = $id . "Form";
+ include __DIR__ . "/auto_windowing.php";
+ })();
+?>
+
+ setTimeout(function () {
+ $('#<?=$id?>Form').trigger('resize');
+ }, 50);
+ }, 10);
+ // });
+
+ e_invalid = "Ungültige Eingaben gefunden.";
+
+ function writeChanges() {
+ if (!ckvalid()) return false;
+
+ $('.changed input, .changed select').not('.ql-tooltip').not('.ql-hidden').each(function () {
+ var n = $(this).attr('name');
+ var v = $(this).val();
+ if ($(this).is('input[type=checkbox]'))
+ v = $(this).prop('checked') ? 1 : 0;
+
+ var noerror = false;
+ var number = $(this).is('select') || $(this).parent().hasClass('number') || $(this).is('input[type=checkbox]');
+ if ($(this).parent().hasClass('string')) number = false;
+
+ $.ajaxSetup({async:false});
+ $.ajax({
+ type: 'POST',
+ url: '<?=$updater?>',
+ data: {
+ table: '<?=$entrytable?>',
+ id: '<?=$l->ID?>',
+ n: n,
+ v: v,
+ num: number
+ },
+ success: function (d, s, x) {
+ if (d == "true") noerror = true;
+ }
+ });
+ $.ajaxSetup({async:true});
+
+ if (noerror) {
+ $(this).parent().removeClass('changed');
+ } else {
+ alert(unescape('Wert für das Feld "' + n + '" konnte nicht gespeichert werden.'));
+ }
+ });
+
+ $('.ql-editor.changed').each(function () {
+ var n = $(this).parent().attr('name');
+ var v = $(this).html();
+
+ var noerror = false;
+ $.ajaxSetup({async:false});
+ $.ajax({
+ type: 'POST',
+ url: '<?=$updater?>',
+ data: {
+ table: '<?=$entrytable?>',
+ id: '<?=$l->ID?>',
+ n: n,
+ v: v.trim().replace(/<p><br><\/p>$/, ""),
+ num: false
+ },
+ success: function (d, s, x) {
+ if (d == "true") noerror = true;
+ }
+ });
+ $.ajaxSetup({async:true});
+
+ if (noerror) {
+ $(this).removeClass('changed');
+ } else {
+ alert(unescape('Text im Feld ' + n + ' konnte nicht gespeichert werden.'));
+ }
+ });
+
+ $('.editorcontainer.ace .changed').each(function () {
+ var n = $(this).attr('name');
+ var v = aceeditors[0].getSession().getValue();
+
+ var noerror = false;
+ $.ajaxSetup({async:false});
+ $.ajax({
+ type: 'POST',
+ url: '<?=$updater?>',
+ data: {
+ table: '<?=$entrytable?>',
+ id: '<?=$l->ID?>',
+ n: n,
+ v: v.trim().replace(/<p><br><\/p>$/, ""),
+ num: false
+ },
+ success: function (d, s, x) {
+ if (d == "true") noerror = true;
+ }
+ });
+ $.ajaxSetup({async:true});
+
+ if (noerror) {
+ $(this).removeClass('changed');
+ } else {
+ alert(unescape('Text im Feld ' + n + ' konnte nicht gespeichert werden.'));
+ }
+ });
+
+ if ($('.changed input, .changed select, .ql-editor.changed').length === 0) {
+ $('#<?=$id?>Form .savebutton').hide();
+ location.reload();
+ }
+
+ return false;
+ }
+<? minEnd(); ?>
+</script>
+
+<style>
+.ql-editor {
+ padding: 0.3em !important;
+}
+.ql-toolbar.ql-snow {
+ padding: 0.1em !important;
+ background: lightgrey;
+}
+</style>
+
+<?
+})();
+?>
diff --git a/autopdf.php b/autopdf.php
new file mode 100644
index 0000000..9afe246
--- /dev/null
+++ b/autopdf.php
@@ -0,0 +1,79 @@
+<?php
+/*
+ * 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.
+ */
+?>
+<?
+
+(function () use ($id, $pdf_sql, $pdf_file, $payload_sql, $independent_payload_sql, $payload_sql_order, $independent_payload_sql_order, $post_sql) {
+ if (!isset($pdf_file)) $pdf_file = "bescheid";
+ if (!isset($payload_sql_order)) $payload_sql_order = "";
+ if (!isset($independent_payload_sql)) $independent_payload_sql = "";
+ if (!isset($independent_payload_sql_order)) $independent_payload_sql_order = "";
+?>
+
+<div style="display: none;">
+ <form id="dopdf_<?=$id?>" action="/db/main/pdf.php" method="post">
+ <input id="dopdf_ids_<?=$id?>" name="ids" type="hidden" value="">
+ <input id="dopdf_sql_<?=$id?>" name="sql" type="hidden" value="<?=$pdf_sql?>">
+ <input id="dopdf_postsql_<?=$id?>" name="postsql" type="hidden" value="<?=$post_sql?>">
+ <input id="dopdf_file_<?=$id?>" name="file" type="hidden" value="<?=$pdf_file?>">
+
+ <input id="dopdf_payload_sql_<?=$id?>" name="payload_sql" type="hidden" value="<?=$payload_sql?>">
+ <input id="dopdf_payload_sql_order_<?=$id?>" name="payload_sql_order" type="hidden" value="<?=$payload_sql_order?>">
+ <input id="dopdf_independent_payload_sql_<?=$id?>" name="independent_payload_sql" type="hidden" value="<?=$independent_payload_sql?>">
+ <input id="dopdf_independent_payload_sql_order_<?=$id?>" name="independent_payload_sql_order" type="hidden" value="<?=$independent_payload_sql_order?>">
+ <input id="dopdf_nocount_<?=$id?>" name="nocount" type="hidden" value="<?=$nocount?>">
+ </form>
+</div>
+
+<script>
+<? minStart(); ?>
+
+function doPDF_<?=$id?>(ids) {
+ if (typeof ids === 'undefined') {
+ if (typeof autotables === 'undefined') {
+ ids = [];
+ $('#<?=$id?> tr.selected').each(function () {
+ ids.push($(this).attr('data-id'));
+ });
+ } else {
+ var g = $.grep(autotables['<?=$id?>'].table().rows().nodes(), function (e) {
+ return e.classList.contains('selected');
+ });
+ ids = [];
+ $.each(g, function (i, e) {
+ var ee = false;
+ var eee = false;
+ try { ee = e.getElementsByClassName('ajaxident')[0].getAttribute('data-id'); } catch (e) { e; }
+ try { eee = e.getElementsByClassName('ajaxident')[0].getAttribute('data-persid'); } catch (e) { e; }
+ if (ee) ids.push(ee);
+ else if (eee) ids.push(eee);
+ else if (e.getAttribute('data-id')) ids.push(e.getAttribute('data-id'))
+ else ids.push(e.getAttribute('data-persid'));
+ });
+ }
+ }
+ $('#dopdf_ids_<?=$id?>').val(ids);
+ $('#dopdf_<?=$id?>').submit();
+}
+
+if ($('#<?=$id?>Actions').length > 0) {
+ $('#<?=$id?>Actions').append('&nbsp;');
+ $('<button class="medium" onclick="return doPDF_<?=$id?>();"><i class="fas fa-file-pdf"></i> PDF generieren</button>').appendTo('#<?=$id?>Actions');
+}
+
+<? minEnd(); ?>
+</script>
+
+<?
+})();
+?>
diff --git a/autotable.php b/autotable.php
new file mode 100644
index 0000000..93307f7
--- /dev/null
+++ b/autotable.php
@@ -0,0 +1,1103 @@
+<?php
+/*
+ * 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.
+ */
+?>
+<?
+/* vim: set ts=4 sw=4 et : */
+
+require_once __DIR__ . "/auto_common.php";
+
+(function () use ($r, $id, $title, $subtitle, &$num_rows, $top, $bottom, $alwaysbottom, $off, $order, $checkboxes, $links, $rowcallback, $norefresh, $editable, $types, $entrytable, $callback, $callback_heading, $json, $jsoncallback, $pii, $nospinner, $ajax, $sql, $colsql, $makeseq, $thdef, $nochosen, $idcell, $idcellreal, $nofilter, $getthdef, $insert, $noautolinks, $nodefaultlinks) {
+ if (!isset($id) || $id == "") {
+ $pwsql = stristr($sql, ' WHERE ', true);
+ if ($pwsql === false || $pwsql === '') {
+ $pwsql = $sql;
+ }
+ $id = substr(sha1($pwsql), 0, 16);
+ }
+
+ if (!isset($order) || $order == "")
+ $order = '[[1, "desc"]]';
+
+ if (!isset($colsql) || $colsql == "")
+ $colsql = $sql . " LIMIT 1";
+
+ if (!isset($nospinner))
+ $nospinner = false;
+
+ if (!isset($ajax))
+ $ajax = false;
+
+ if (!isset($thdef))
+ $thdef = false;
+
+ if (!isset($nochosen))
+ $nochosen = false;
+
+ if (!isset($nofilter))
+ $nofilter = false;
+
+ if (!$nospinner)
+ include_once __DIR__ . '/loading_modal.php';
+
+ if (!isset($editable))
+ $editable = [];
+ if (!isset($types))
+ $types = [];
+ if (!isset($json))
+ $json = [];
+ if (!isset($off))
+ $off = false;
+ if (!isset($makeseq))
+ $makeseq = false;
+ if (!isset($alwaysbottom))
+ $alwaysbottom = false;
+
+ $pii = getPii($pii);
+
+ if (!isset($nodefaultlinks))
+ $nodefaultlinks = false;
+
+ if (!$nodefaultlinks)
+ $links = getLinks($links);
+
+ if (!isset($idcell))
+ $idcell = false;
+
+ if (!isset($idcellreal))
+ $idcellreal = $idcell;
+
+ if (isset($getthdef) && $getthdef) {
+ global $mysqli;
+ $stmt = $mysqli->prepare($sql);
+ $stmt->execute();
+ $r = $stmt->get_result();
+ }
+
+ if (!isset($noautolinks))
+ $noautolinks = false;
+
+ if (!isset($insert))
+ $insert = pathinfo(basename(__FILE__), PATHINFO_FILENAME);
+?>
+
+ <div class="autotable_containment" id="autotable_containment_<?=$id?>">
+ <div style="padding: 0; margin: 0;"></div>
+
+<?
+ if (isset($title)) {
+?>
+ <h2 id="anchor_<?=$id?>" data-table="<?=$id?>" class="prevent autotable_title" onclick="toggleTable('<?=$id?>');" style="cursor: pointer;">
+ <?=$title?>
+ <span id="<?=$id?>Count"></span>
+ <i <? if ($off) { echo 'style="display: none;"'; } ?> id="<?=$id?>Table_off" class="fas fa-toggle-on"></i>
+ <i <? if (!$off) { echo 'style="display: none;"'; } ?> id="<?=$id?>Table_on" class="fas fa-toggle-off"></i>
+ </h2>
+
+ <script>
+<? minStart(); ?>
+ function toggleTable(id, forceon) {
+ if (typeof forceon === 'undefined')
+ forceon = false;
+
+ if (forceon) {
+ $('#' + id + 'Table').show();
+ $('#' + id + 'Table_on').hide();
+ $('#' + id + 'Table_off').show();
+ } else {
+ $('#' + id + 'Table').toggle();
+ $('#' + id + 'Table_on').toggle();
+ $('#' + id + 'Table_off').toggle();
+ }
+ $('#' + id).trigger('resize');
+
+ if ($('#' + id + 'Table:visible')[0]) localStorage.setItem(location.href + '_' + id + 'Table', 'true');
+ else localStorage.setItem(location.href + '_' + id + 'Table', 'false');
+
+ setTimeout(function () {
+ $(window).resize();
+ }, 100);
+ }
+
+ $(document).ready(function () {
+<? if ($off) { ?>
+ if (localStorage.getItem(location.href + '_<?=$id?>Table') == 'true')
+ toggleTable('<?=$id?>');
+<? } else { ?>
+ if (localStorage.getItem(location.href + '_<?=$id?>Table') == 'false')
+ toggleTable('<?=$id?>');
+<? } ?>
+ });
+<? minEnd(); ?>
+ </script>
+<?
+ }
+?>
+
+ <div <? if ($off) { echo 'style="display: none;"'; } ?> id="<?=$id?>Table">
+<? if (isset($subtitle)) { ?>
+ <?=$subtitle?>
+<? } ?>
+ <span id="<?=$id?>Empty" style="display: none; padding-left: .5em;">Keine Ergebnisse!</span>
+ <div id="<?=$id?>TableInner">
+ <progress id="<?=$id?>TableProgress"></progress>
+
+<?
+ if (isset($top)) echo $top;
+
+ if ((!($ajax && $thdef)) && ($r->num_rows < 1)) {
+?>
+ <span style="padding-left: .5em;">Keine Ergebnisse!</span>
+ <script>
+<? minStart(); ?>
+ $('#<?=$id?>TableProgress').hide();
+ $('#<?=$id?>Count').html('<sup><small>(0/0)</small></sup>');
+ $('body').css('overflow', 'auto');
+ $(document).ready(function () { $('#<?=$id?>Actions').hide(); });
+<? if (!$nospinner) { ?>
+ $('#loading_modal').hide();
+<? } ?>
+<? minEnd(); ?>
+ </script>
+<?
+ if ($alwaysbottom) {
+?>
+ <?=$bottom?>
+<?
+ }
+?>
+ </div>
+ </div>
+<?
+ } else {
+ if (!$norefresh) {
+?>
+ <div class="small" id="tablereordered-<?=$id?>" style="display: none; color: darkred; margin-top: .5em; margin-bottom: .5em;">
+ <b>Spalten neu geordnet. Für die Filterfunktionen muss anschließend die <button class="small" style="background: orange;" onclick='window.location = window.location;'>Seite neu geladen werden</button></b>
+ </div>
+
+ <button class="small" id="tablereload-<?=$id?>"><i class="fas fa-sync-alt"></i> Aktualisieren</button>
+ <button class="small" id="tablereset-<?=$id?>">
+ <div class="tooltip">
+ <span class="tooltiptext">Alle Einstellungen und Filter verwerfen, anschlie&szlig;end Tabelle neuladen</span>
+ <i class="fas fa-undo-alt"></i> Reset
+ </div>
+ </button>
+ <button class="small lila" id="tableexcel-<?=$id?>">
+ <div class="tooltip"><span class="tooltiptext">Aktuelle Seite nach Excel exportieren</span><i class="fas fa-magic"></i> <i class="fas fa-eye"></i> <i class="fas fa-file-excel"></i></div>
+ </button>
+<? if ($checkboxes) { ?>
+ <button class="small lila" id="tableexcelsel-<?=$id?>">
+ <div class="tooltip"><span class="tooltiptext">Markierte nach Excel exportieren</span><i class="fas fa-magic"></i> <i class="fas fa-check"></i> <i class="fas fa-file-excel"></i></div>
+ </button>
+<? } ?>
+<?
+include __DIR__ . '/autotable_fontsettings.php';
+include __DIR__ . '/autotable_explain.php';
+?>
+
+ <div style="display: none;">
+ <form id="<?=$id?>Excel" action="/db/main/excel.php" method="post">
+ <input id="<?=$id?>ExcelName" name="name" type="hidden" value="">
+ <input id="<?=$id?>ExcelHeading" name="heading" type="hidden" value="">
+ <input id="<?=$id?>ExcelLines" name="lines" type="hidden" value="">
+ </form>
+ </div>
+
+<?
+ }
+?>
+ <style>
+ #<?=$id?>_wrapper .dataTables_scrollBody {
+ max-height: calc(100vh - 16em);
+ }
+ </style>
+
+ <table id="<?=$id?>" border="0" class="autotable display nowrap" style="width: 100%; display: none;">
+ <thead>
+ <tr>
+<?
+ if ($checkboxes || $ajax) {
+?>
+ <th></th>
+<?
+ }
+ if ($makeseq) {
+?>
+ <th>#</th>
+<?
+ }
+
+ $autotable_i = 0;
+ if (!$thdef) {
+ $l = $r->fetch_object();
+ foreach ($l as $k => $v) {
+ $bk = str_replace(' ', '<br />', $k);
+ if (substr($k, 0, 1) === '%') {
+ $k = substr($k, 1);
+ }
+
+ if ($editable[$k]) {
+?>
+ <th data-realname="<?=$editable[$k]?>" data-displayname="<?=$k?>" class="autotable_<?=$id?>_<?=$editable[$k]?> at_col_<?=$editable[$k]?> editable <? if (isset($types[$k]) && is_array($types[$k])) { ?>select no-sort<? } ?>"><?=$bk?>✎</th>
+<?
+ } else {
+?>
+ <th data-displayname="<?=$k?>" class="autotable_<?=$id?>_<?=$k?> at_col_<?=$k?> <? if (in_array($k, $editable)) { ?>editable<? } ?> <? if (isset($types[$k]) && is_array($types[$k])) { ?>select no-sort<? } ?>"><?=$bk?><? if (in_array($k, $editable)) { ?>✎<? } ?></th>
+<?
+ }
+ }
+ if ($callback) {
+?>
+ <th><?=$callback_heading?></th>
+<?
+ }
+ } else {
+ foreach ($thdef as $k) {
+ $bk = str_replace(' ', '<br />', $k);
+ if ($editable[$k]) {
+?>
+ <th data-realname="<?=$editablele[$k]?>" data-displayname="<?=$k?>" class="autotable_<?=$id?>_<?=$editable[$k]?> at_col_<?=$editable[$k]?> editable"><?=$bk?>✎</th>
+<?
+ } else {
+?>
+ <th data-displayname="<?=$k?>" class="autotable_<?=$id?>_<?=$k?> at_col_<?=$k?> <? if (in_array($k, $editable)) { ?>editable<? } ?>"><?=$bk?><? if (in_array($k, $editable)) { ?>✎<? } ?></th>
+<?
+ }
+ }
+ }
+?>
+ </tr>
+ </thead>
+
+<? if (!$ajax) { ?>
+ <script>
+ $('#<?=$id?>TableProgress').prop('max', <?=$r->num_rows?>);
+ $('#<?=$id?>TableProgress').prop('value', 0);
+ </script>
+ <tbody>
+<?
+ do {
+ $autotable_i++;
+ if (!$nospinner && $autotable_i % 100 == 0) {
+?>
+ <script>$('#progress').html('<?=$autotable_i?> Einträge von <?=$r->num_rows?> verarbeitet'); $('#<?=$id?>TableProgress').prop('value', <?=$autotable_i?>);</script>
+<?
+ }
+ if (!$nospinner && $autotable_i % 100 == 0 && $autotable_i + 350 >= $r->num_rows) {
+?>
+ <script>$('#progress').html('<?=$autotable_i?> Einträge von <?=$r->num_rows?> verarbeitet, initialisiere Tabelle'); $('#<?=$id?>TableProgress').prop('value', <?=$autotable_i?>);</script>
+<?
+ }
+ if (!$nospinner && $autotable_i == $r->num_rows) {
+?>
+ <script>$('#progress').html('Alle <?=$r->num_rows?> Einträge verarbeitet, initialisiere Tabelle'); $('#<?=$id?>TableProgress').prop('value', <?=$autotable_i?>);</script>
+<?
+ }
+ if ($nospinner) {
+?>
+ <script>$('#<?=$id?>TableProgress').prop('value', <?=$autotable_i?>);</script>
+<?
+ }
+
+ $dataid = "";
+ if ($idcell) $dataid = $l->$idcell;
+ else if (isset($l->ID)) $dataid = $l->ID;
+ else if (isset($l->uid)) $dataid = $l->uid;
+ else if (isset($l->UID)) $dataid = $l->UID;
+ else if (isset($l->id)) $dataid = $l->id;
+ else if (isset($l->PersID)) $dataid = $l->PersID;
+ else if (isset($l->StipID)) $dataid = $l->StipID;
+ else if (isset($l->EventID)) $dataid = $l->EventID;
+ else if (isset($l->FoerdID)) $dataid = $l->FoerdID;
+ else if (isset($l->OrgaID)) $dataid = $l->OrgaID;
+ else if (isset($l->NotizID)) $dataid = $l->NotizID;
+ else if (isset($l->EmailID)) $dataid = $l->EmailID;
+ else if (isset($l->AppNewsID)) $dataid = $l->AppNewsID;
+ else if (isset($l->AppSettingID)) $dataid = $l->AppSettingID;
+ else if (isset($l->AppProfileID)) $dataid = $l->AppProfileID;
+ else if (isset($l->ETID)) $dataid = $l->ETID;
+
+ if ($makeseq) $dataid = $autotable_i;
+
+ $persid = "";
+ if (isset($l->PersID)) $persid = $l->PersID;
+
+ $stipid = "";
+ if (isset($l->StipID)) $stipid = $l->StipID;
+
+ $orgaid = "";
+ if (isset($l->OrgaID)) $orgaid = $l->OrgaID;
+
+ $foerdid = "";
+ if (isset($l->FoerdID)) $foerdid = $l->FoerdID;
+
+ $eventid = "";
+ if (isset($l->EventID)) $eventid = $l->EventID;
+?>
+ <tr data-seq="<?=$autotable_i?>" data-id="<?=$dataid?>" data-persid="<?=$persid?>" data-stipid="<?=$stipid?>" data-orgaid="<?=$orgaid?>" data-foerdid="<?=$foerdid?>" data-eventid="<?=$eventid?>">
+<?
+ if ($checkboxes) {
+?>
+ <td></td>
+<?
+ }
+ if ($makeseq) {
+?>
+ <td><a href="#<?=$autotable_i?>"><?=$autotable_i?></a></td>
+<?
+ }
+
+ foreach ($l as $k => $v) {
+ if (substr($k, 0, 1) === '%') {
+ $v = utf8_encode(urldecode($v));
+ $k = substr($k, 1);
+ }
+ if (isset($links[$k])) {
+?>
+ <td class="autotable_<?=$id?>_<?=$k?>"><? if ($v != 0 || $v != '') { ?><a class="autolink" href="<?=$links[$k]?><?=$v?><? if (isset($_GET['iframe'])) { ?>?iframe<? } ?>"><?=$v?></a><? } ?></td>
+<?
+ } else {
+?>
+ <td class="autotable_<?=$id?>_<?=$k?> <? if (in_array($k, $pii)) { echo "pii"; } ?>" data-n="<? if ($editable && $editable[$k]) { echo $editable[$k]; } else { echo $k; } ?>">
+<?
+ if (in_array($k, $editable) || $editable[$k]) {
+ if (!isset($types[$k])) {
+?>
+ <input type="text" class="editable" style="width: auto; font-size: 1em; border: 0; padding: 0; margin: 0; background: rgba(255, 255, 255, 0.1);" size="<?=strlen($v)?>" value="<?=$v?>"><span style="display: none;" class="indicator"><?=$v?></span>
+<?
+ } else if ($types[$k] == 'date') {
+?>
+ <input type="date" class="editable" value="<?=$v?>"><span style="display: none;" class="indicator"><?=$v?></span>
+<?
+ } else if ($types[$k] == 'checkbox') {
+?>
+ <input type="checkbox" class="editable" <? if ($v) { echo "checked"; } ?>><span style="display: none;" class="indicator"><?=$v?></span>
+<?
+ } else if ($types[$k] == 'checkboxinverse') {
+?>
+ <input type="checkbox" class="editable inverse" <? if ($v) { echo "checked"; } ?>><span style="display: none;" class="indicator"><?=$v?></span>
+<?
+ } else if (is_array($types[$k])) {
+?>
+ <select class="editable <? if ($types[$k][0] == 'reload') { ?>reload<? } ?>">
+ <option value=""></option>
+<?
+ foreach($types[$k][1] as $a => $b) {
+?>
+ <option value="<?=$b['id']?>" <? if ($b['id'] == $v) { ?>selected<? } ?>><?=$a?><? if (isset($b['cn'])) { ?> - <?=$b['cn']?><? } ?></option>
+<?
+ }
+?>
+ </select>
+<?
+ }
+ } else if (in_array($k, $json)) {
+ $jsoncallback($k, json_decode($v));
+ } else {
+?>
+ <?=$v?>
+<?
+ }
+?>
+
+ </td>
+<?
+ }
+ }
+
+ if ($callback) {
+?>
+ <td>
+<?
+ $callback($dataid);
+?>
+ </td>
+<?
+ }
+?>
+ </tr>
+<?
+ } while (($l = $r->fetch_object()))
+?>
+ </tbody>
+<? } else { /* !$ajax */ ?>
+ <script>$('#progress').html('Initialisiere Tabelle');</script>
+<? } ?>
+
+ <tfoot>
+ <tr></tr>
+ </tfoot>
+ </table>
+
+ <script>$('#<?=$id?>TableProgress').removeProp('value');</script>
+ <script>$('#<?=$id?>TableProgress').hide();</script>
+
+<?
+ if (isset($bottom)) echo $bottom;
+?>
+
+ </div>
+ </div>
+ </div>
+
+ <script>
+<? minStart(); ?>
+ $('.prevent').on('mousedown', function (e) {
+ var doc = $(document);
+ e.preventDefault();
+ var handler = function () {
+ e.preventDefault();
+ };
+ doc.on('mousemove', handler);
+ doc.one('mouseup', function (e) {
+ doc.off('mousemove', handler);
+ });
+ });
+
+ (function () {
+<? if ($ajax) {
+ $ajaxsql = ['sql' => $sql, 'colsql' => $colsql];
+?>
+ var cDefs = [];
+<?
+ if (!$thdef) {
+?>
+ $.ajaxSetup({async:false});
+ $.ajax({
+ type: 'POST',
+ url: '/db/json/<?=$id?>?columns',
+ data: <?=json_encode($ajaxsql)?>,
+ success: function (d, s, x) {
+ try {
+ cDefs = JSON.parse(d);
+ } catch (e) {
+ alert("Interner Fehler. Kann Tabellenspaltendefinition nicht erzeugen.");
+ $('#loading_modal').hide();
+ $('body').css('border', '10px dashed red');
+ }
+ }
+ });
+ $.ajaxSetup({async:true});
+<?
+ } else {
+ $columns = [];
+ $col = new stdClass();
+ $col->data = 'ajaxident';
+ $columns[] = $col;
+
+ foreach ($thdef as $k) {
+ $col = new stdClass();
+ $col->data = $k;
+ $columns[] = $col;
+ }
+?>
+ cDefs = <?=json_encode($columns)?>;
+<?
+ }
+?>
+<? } ?>
+ var tf = $('#<?=$id?> tfoot tr');
+ $('#<?=$id?> thead th').each(function (i) {
+ var classes = "filter";
+ if ($(this).hasClass('editable')) classes += " editable";
+ if ($(this).hasClass('select')) classes += " select";
+ if ($(this).hasClass('no-sort')) classes += " no-sort";
+ tf.append('<th class="' + classes + '"></th>');
+ });
+
+ var writeChanges = function (id, n, v, num) {
+ var table = '<?=$entrytable?>';
+ var noerror = false;
+ $.ajaxSetup({async:false});
+ $.ajax({
+ type: 'POST',
+ url: '/db/main/update.php',
+ data: {
+ table: table,
+ id: id,
+ n: n,
+ v: v,
+ num: num
+ <? if ($idcellreal) { ?>, idcell: "<?=$idcellreal?>"<? } ?>
+ },
+ success: function (d, s, x) {
+ if (d == "true") noerror = true;
+ }
+ });
+ $.ajaxSetup({async:true});
+
+ if (noerror) {
+ return true;
+ } else {
+ alert(unescape('Wert\n "' + v + '"\nfür das Feld\n "' + n + '"\nin der Tabelle\n "' + table + '"\nkonnte nicht gespeichert werden.'));
+ return false;
+ }
+ };
+
+ var setupEditableChange = function () {
+ $('#<?=$id?> tbody input.editable, #<?=$id?> tbody select.editable').on('change', function () {
+ $(this).parent().addClass('changed');
+ console.debug('some change handler just fired');
+ var id = $(this).parent().parent().attr('data-id');
+ var n = $(this).parent().attr('data-n');
+ var v = $(this).val();
+ num = false;
+ if ($(this).is('input[type=checkbox]')) {
+ num = true;
+ v = $(this).prop('checked') ? 1 : 0;
+ }
+
+ if (writeChanges(id, n, v, num)) {
+ $(this).parent().removeClass('changed');
+ $(this).parent().removeClass('changesFailed');
+ $(this).parent().addClass('changesCommitted');
+ if ($(this).hasClass('reload'))
+ location.href = location.href;
+ } else {
+ $(this).parent().removeClass('changed');
+ $(this).parent().removeClass('changesCommitted');
+ $(this).parent().addClass('changesFailed');
+ }
+ });
+ };
+
+ var scramblePii = function () {
+ $('#<?=$id?> td.pii').each(function (i, e) {
+ e.innerHTML = nrot3(nshuffle(e.innerHTML));
+ });
+ };
+
+ var updateCount = function () {
+ var t = '' + dt.page.info().recordsDisplay + '';
+ t += '/' + dt.rows().count() + '';
+ // t += '/' + dt.rows(':selected').count() + '';
+ $('#<?=$id?>Count').html(' <sup><small>(' + t + ')</small></sup>');
+ if (ajaxdone && dt.rows().count() < 1) {
+ $('#<?=$id?>TableInner').hide();
+ $('#<?=$id?>Empty').show();
+ } else {
+ $('#<?=$id?>TableInner').show();
+ $('#<?=$id?>Empty').hide();
+ }
+ };
+
+ var inputTriggers = function () {
+ $('#<?=$id?> tbody td input[type=checkbox]').one('click', function () {
+ $(this).next().html($(this).prop('checked') ? 1 : 0);
+ });
+ }
+
+ var restoreState = function () {
+ var state = dt.state.loaded();
+ if (state) {
+ dt.columns().eq(0).each(function (colIdx) {
+ var colSearch = state.columns[colIdx].search;
+ var realIdx = state.ColReorder.indexOf(colIdx);
+
+ if (colSearch.search) {
+ dt.columns().footer()[realIdx].getElementsByTagName('input')[0].value = colSearch.search;
+ dt.columns().footer()[realIdx].classList.add('filteron');
+ dt.columns().header()[realIdx].classList.add('filteron');
+
+ $('tfoot th', dt.columns().footer()).eq(realIdx).find('input')
+ .val(colSearch.search)
+ .trigger('keyup')
+ .parent().addClass('filteron');
+ }
+ });
+ dt.draw();
+ }
+ }
+ window.restorestate = restoreState;
+
+ $.fn.dataTable.ext.buttons.selectFiltered = {
+ className: 'select_filtered'
+ };
+
+ $('#<?=$id?>').show();
+ var ajaxdone = false;
+ var dt = $('#<?=$id?>').DataTable({
+ "language": {
+ url: "/3rdparty/German.json",
+ processing: '<i class="fa fa-spinner fa-spin fa-3x fa-fw"></i><span class="sr-only">Lade...</span>'
+ },
+ "order": <?=$order?>,
+ "scrollX": true,
+ "colReorder": {
+ 'enable': true,
+<? if ($checkboxes) { ?>
+ 'fixedColumnsLeft': 1
+<? } ?>
+ },
+ "lengthMenu": [5, 10, 20, 50, 100, 500, 1000],
+ "stateSave": true,
+ "dom": 'Blfrtip',
+<? if ($checkboxes) { ?>
+ "buttons": ['columnsToggle', 'selectAll', { text: 'selectFiltered', extend: 'selectFiltered', action: function (e, dt) { dt.rows({filter: 'applied'}).select(); }}, 'selectNone'],
+<? } else { ?>
+ "buttons": ['columnsToggle'],
+<? } ?>
+ "processing": true,
+<? if ($ajax) {
+ ?>
+ "ajax": {
+ 'url': '/db/json/<?=$id?>',
+ 'type': 'POST',
+ 'data': <?=json_encode($ajaxsql)?>,
+ 'dataSrc': function (d) {
+ ajaxdone = true;
+ return d.data;
+ },
+ },
+ columns: cDefs,
+<? } ?>
+ "initComplete": function () {
+ console.log('init complete <?=$id?>');
+
+ this.api().columns().every(function () {
+ var column = this;
+ var idx = column.index();
+
+ var title = dt.columns().header()[idx].innerHTML.replaceAll('<br>', ' ');
+ var header = dt.columns().header()[idx];
+ if (title == "") return;
+
+ if (dt.columns().header()[idx].classList.contains('select')) return;
+
+<? if (!$nofilter) { ?>
+ var input = $('<input type="text" data-index="' + idx + '" placeholder="ᗊ ' + title + '" class="filter"><br />')
+ .appendTo($(column.footer()).empty())
+ .on('change keyup', function () {
+ $(this).next().next().val('').removeClass('filteron');
+ column.search(this.value, true).draw();
+
+ if (this.value != '') {
+ this.parentElement.classList.add('filteron');
+ header.classList.add('filteron');
+ } else {
+ this.parentElement.classList.remove('filteron');
+ header.classList.remove('filteron');
+ }
+ });
+<? } ?>
+
+ if (title.match(/ID$/)) return;
+ if (title.match(/^id$/)) return;
+ if (title.match(/^uid$/i)) return;
+
+<? if (!$nochosen) { ?>
+ var entries = column.data();
+ entriesclean = [];
+ $.each(entries, (function (i, d) {
+ if (d === "") return;
+ if ($('<div />').html(d).find('select').length > 0) {
+ entriesclean.push($('<div />').html(d).find('select option:selected').text());
+ } else {
+ entriesclean.push($('<div />').html(d).text());
+ }
+ }));
+
+ var uni = _.uniq(entriesclean).sort(function (a, b) {
+ if (a.toLowerCase() < b.toLowerCase()) return -1;
+ if (a.toLowerCase() > b.toLowerCase()) return 1;
+ return 0;
+ });
+ if (uni.length > 600) return;
+
+ var select = $('<select data-index="' + idx + '" class="filter" multiple><option value=""></option><option value="{{{}}}">[leere]</option><option value="}}}{{{">[nicht leere]</option></select>')
+ .appendTo($(column.footer()))
+ .on('change select', function () {
+ var v = $(this).val();
+ var val = "";
+ if (typeof v === 'string') {
+ if (v == '{{{}}}')
+ val = '^$';
+ else if (v == '}}}{{{')
+ val = '.+';
+ else
+ val = $.fn.dataTable.util.escapeRegex(v);
+ } else if (typeof v === 'object') {
+ $.each(v, function (i, e) {
+ console.log(e);
+ if (val != "")
+ val += '|';
+ if (e == '{{{}}}')
+ val += '^$';
+ else if (e == '}}}{{{')
+ val += '.+';
+ else
+ val += $.fn.dataTable.util.escapeRegex(e);
+ });
+ }
+
+ console.log(val);
+ if ($(this).parent().hasClass('select')) {
+ column.search(val ? val : '', true, true).draw();
+ } else {
+ column.search(val ? '^' + val + '$' : '', true, false).draw();
+ }
+
+ $(this).siblings().val('');
+ $(this).siblings().removeClass('filteron');
+ $(this).parent().removeClass('filteron');
+
+ if (this.value != '') {
+ this.classList.add('filteron');
+ header.classList.add('filteron');
+ } else {
+ this.classList.remove('filteron');
+ header.classList.remove('filteron');
+ }
+ });
+
+ $.each(uni, function (i, d) {
+ if (d == "") return;
+ var t = $('<div />').html(d).text();
+ if (t == "") return;
+ select.append('<option value="' + t + '">' + t + '</option>');
+ });
+<? } ?>
+
+<? if (true || !$ajax) { ?>
+ this.on('column-reorder', function () {
+ $('div#<?=$id?>Table table tfoot').hide();
+ $('div#tablereordered-<?=$id?>').show();
+ console.log('reordered');
+ });
+<? } ?>
+
+ });
+ console.log('after auto search setup <?=$id?>');
+
+<? if ($_SESSION['scramble']) { ?>
+ scramblePii();
+<? } ?>
+
+
+ updateCount();
+ $('body').css('overflow', 'auto');
+<? if (!$nospinner) { ?>
+ $('#loading_modal').hide();
+ $('#progress').html('');
+<? } ?>
+
+ $('#<?=$id?>_wrapper .dt-buttons').before('<div style="display: inline-block; float: left; padding-right: .5em; cursor: pointer;" id="<?=$id?>_column_toggle_on"><div class="tooltip"><span class="tooltiptext">Spaltenauswahl ein-/ausblenden</span><i class="fas fa-toggle-off"></i> <small><i class="fas fa-columns"></i></small> <span></span></div></div>');
+ $('#<?=$id?>_wrapper .dt-buttons').children().first().before('<div style="display: inline-block; float: left; padding-right: .5em; cursor: pointer;" id="<?=$id?>_column_toggle_off"><i class="fas fa-toggle-on"></i> <small><i class="fas fa-columns"></i></small> <span class="column_count"></span> [alle <span id="<?=$id?>_column_all_off"><i class="fas fa-eye-slash"></i></span> <span id="<?=$id?>_column_all_on"><i class="fas fa-eye"></i></span>]</div>');
+
+<? if ($checkboxes) { ?>
+ var btall = $('#<?=$id?>_wrapper .dt-buttons button.buttons-select-all');
+ var btnone = $('#<?=$id?>_wrapper .dt-buttons button.buttons-select-none');
+ var btfiltered = $('#<?=$id?>_wrapper .dt-buttons button.select_filtered');
+ btall.detach();
+ btnone.detach();
+ btfiltered.detach();
+ btall.html('<div class="tooltip"><span class="tooltiptext">Alle auswählen - unabhängig von Filter</span><i class="fas fa-check-double"></i></span></div>');
+ btnone.html('<div class="tooltip"><span class="tooltiptext">Nichts ausw&auml;hlen</span><i class="far fa-square"></i></span></div>');
+ btfiltered.html('<div class="tooltip"><span class="tooltiptext">Alle gefilterten ausw&auml;hlen</span><i class="fas fa-filter"></i><i class="fas fa-check"></i></span></div>');
+
+ btall.removeClass().addClass('medium');
+ btnone.removeClass().addClass('medium');
+ btfiltered.removeClass().addClass('medium');
+ btnone.insertAfter('#<?=$id?>_wrapper');
+ btall.insertAfter('#<?=$id?>_wrapper');
+ btall.after('&nbsp;');
+ btfiltered.insertAfter('#<?=$id?>_wrapper');
+ btfiltered.after('&nbsp;');
+<? } ?>
+
+ $('#<?=$id?>_column_toggle_on').on('click', function () {
+ $('#<?=$id?>_wrapper .dt-buttons').show();
+ $('#<?=$id?>_column_toggle_on').hide();
+ localStorage.setItem('table_<?=$id?>_column_toggles_on', 'true');
+ });
+ $('#<?=$id?>_column_toggle_off').on('click', function () {
+ $('#<?=$id?>_wrapper .dt-buttons').hide();
+ $('#<?=$id?>_column_toggle_on').show();
+ localStorage.setItem('table_<?=$id?>_column_toggles_on', 'false');
+ });
+
+ $('#<?=$id?>_column_all_off').on('click', function (ev) {
+ $('#<?=$id?>Table .dt-buttons button.active').click();
+ setTimeout(function () {
+ $('#<?=$id?>_wrapper .dt-buttons').show();
+ $('#<?=$id?>_column_toggle_on').hide();
+ }, 10);
+ });
+
+ $('#<?=$id?>_column_all_on').on('click', function () {
+ $('#<?=$id?>Table .dt-buttons button').not('.active').click();
+ setTimeout(function () {
+ $('#<?=$id?>_wrapper .dt-buttons').show();
+ $('#<?=$id?>_column_toggle_on').hide();
+ }, 10);
+ });
+
+ if (localStorage.getItem('table_<?=$id?>_column_toggles_on') == 'true') {
+ $('#<?=$id?>_wrapper .dt-buttons').show();
+ $('#<?=$id?>_column_toggle_on').hide();
+ } else {
+ $('#<?=$id?>_wrapper .dt-buttons').hide();
+ $('#<?=$id?>_column_toggle_on').show();
+ }
+
+ var updCount = function () {
+ var t = $('#<?=$id?>_wrapper .dt-buttons').children('.dt-button.buttons-columnVisibility').length;
+ var a = $('#<?=$id?>_wrapper .dt-buttons').children('.dt-button.buttons-columnVisibility.active').length;
+ $('#<?=$id?>_column_toggle_on span.column_count, #<?=$id?>_column_toggle_off span.column_count').html('(' + a + '/' + t + ')');
+ if (a != t) $('#<?=$id?>_column_toggle_on span.column_count, #<?=$id?>_column_toggle_off span.column_count').css('color', 'orange');
+ else $('#<?=$id?>_column_toggle_on span.column_count, #<?=$id?>_column_toggle_off span.column_count').css('color', '');
+ };
+ updCount();
+ $('#<?=$id?>_wrapper .dt-buttons .dt-button').on('click', function () {
+ setTimeout(updCount, 200);
+ });
+
+ $(window).trigger('resize');
+
+<? if (!$nochosen) { ?>
+ $('#<?=$id?>Table .dataTables_scrollFoot select').chosen({allow_single_deselect:true});
+ $('#<?=$id?>Table .dataTables_scrollFoot div.chosen-container').click(function () {
+ $(this).find('.chosen-drop').css('left', $(this).position().left + 12);
+ var h = $(this).find('.chosen-drop').height();
+ $(this).find('.chosen-drop').css('top', Math.abs($(document).scrollTop() - $(this).offset().top + h));
+ //console.log($(this).offset().top + ' ' + $(document).scrollTop() + ' ' + $(this).position().top);
+ });
+<? } ?>
+
+ restoreState();
+ }
+
+ <? if ($checkboxes) { ?>,
+ /* fixedColumns: {
+ leftColumns: 1,
+ rightColumns: 0
+ }, */
+ columnDefs: [ {
+ orderable: false,
+ searchable: false,
+ className: 'select-checkbox',
+ targets: 0
+ }, { targets: 'no-sort', searchable: false, orderable: false }
+ <? if ($ajax) { ?>,
+ {
+ render: function (data, type, row, meta) {
+ var s = '<span style="display: none;" class="ajaxident" data-id="' + data['dataid'] + '"';
+
+ if (data['persid']) s += ' data-persid="' + data['persid'] + '"';
+ if (data['stipid']) s += ' data-stipid="' + data['stipid'] + '"';
+ if (data['orgaid']) s += ' data-orgaid="' + data['orgaid'] + '"';
+ if (data['foerdid']) s += ' data-foerdid="' + data['foerdid'] + '"';
+ if (data['eventid']) s += ' data-eventid="' + data['eventid'] + '"';
+
+ s += '>' + JSON.stringify(data) + '</span>';
+ return s;
+ }, targets: 0
+ },
+ /* -------------- */
+ <?=ajaxLinks();?>
+ /* -------------- */
+ <? } ?>
+ ],
+ select: {
+ style: 'os',
+ selector: 'td:first-child'
+ }
+ <? } else { ?>
+ <? if ($ajax) { ?>,
+ columnDefs: [ {
+ visible: false,
+ searchable: 'false',
+ targets: 0
+ },
+ /* -------------- */
+ <?=ajaxLinks();?>
+ /* -------------- */
+ ]
+ <? } ?>
+ <? } ?>,
+ rowCallback: function (a, b, c, d, e, f, g, h, i, j) {
+ updateCount();
+ //inputTriggers();
+ <? if ($rowcallback) { ?>
+ <?=$rowcallback?>(a, b, c, d, e, f, g, h, i, j);
+ <? } ?>
+ <? if ($_SESSION['scramble']) { ?>
+ scramblePii();
+ <? } ?>
+ <? if ($ajax) { ?>
+ <? } ?>
+ }
+ });
+ if (typeof window.autotables === 'undefined')
+ window.autotables = [];
+ window.autotables['<?=$id?>'] = dt;
+
+
+ $('.autotable th').each(function (i) {
+ if ($(this).text().includes("AnfrageWF")) $(this).attr('title', 'Anfrage (Weiter-)-finazierung');
+ if ($(this).text().includes("apstip")) $(this).attr('title', 'Ansprechpartner beim Förderer für die Stipendiaten');
+ if ($(this).text().includes("apsf")) $(this).attr('title', 'Ansprechpartner beim Förderer für den Studienfonds OWL');
+ if ($(this).text().includes("apop")) $(this).attr('title', 'Ansprechpartner operativ');
+ if ($(this).text().includes("apstr")) $(this).attr('title', 'Ansprechpartner strategisch');
+ if ($(this).text().includes("apfi")) $(this).attr('title', 'Ansprechpartner beim Förderer für Finanzen');
+
+ if ($(this).text().includes("elsv")) $(this).attr('title', 'Einladung Stipendienvergabe');
+ if ($(this).text().includes("elfa")) $(this).attr('title', 'Einladung Fördereraustausch');
+ if ($(this).text().includes("elsft")) $(this).attr('title', 'Einladung SFT');
+ if ($(this).text().includes("elosv")) $(this).attr('title', 'Einladung Online-Stipendienvergabe');
+
+ if ($(this).text().includes("ernl")) $(this).attr('title', 'Erhalten Newsletter');
+ if ($(this).text().includes("erwk")) $(this).attr('title', 'Erhalten Weihnachtskarte');
+ if ($(this).text().includes("erjb")) $(this).attr('title', 'Erhalten Jahresbericht');
+ if ($(this).text().includes("erwm")) $(this).attr('title', 'Erhalten Willkommensmail Förderjahresbeginn');
+
+ });
+
+ $('#tablereset-<?=$id?>').click(function () {
+ localStorage.removeItem('table_<?=$id?>_column_toggles_on');
+ dt.state.clear();
+ window.location.reload();
+ });
+ $('#tablereload-<?=$id?>').click(function () {
+<? if (!$ajax) { ?>
+ window.location.reload();
+<? } else { ?>
+ window.ajaxSpSec = setTimeout(function () { $('#ajaxspinner').show(); }, 800);
+ dt.ajax.reload();
+ $(this).fadeToggle().fadeToggle();
+<? } ?>
+ });
+
+ var tableexcel = function (sel) {
+ if (typeof sel === 'undefined')
+ sel = false;
+
+ var heading = [];
+ $('#<?=$id?> thead tr').first().find('th').not('.select-checkbox').each(function (i, e) {
+ //heading.push(e.textContent.trim());
+ heading.push(e.getAttribute('data-displayname'));
+ });
+
+ var lines = [];
+ var selector = '#<?=$id?> tbody tr';
+ if (sel) {
+ selector = $.grep(autotables['<?=$id?>'].table().rows().nodes(), function (e) {
+ return e.classList.contains('selected');
+ });
+ }
+ $(selector).each(function (i, e) {
+ var line = [];
+ $(this).find('td').not('.select-checkbox').each(function (ii, ee) {
+ if (ee.getElementsByTagName('select')[0]) {
+ var sel = ee.getElementsByTagName('select')[0];
+ line.push(sel.options[sel.selectedIndex].innerText.trim());
+ } else {
+ line.push(ee.textContent.trim());
+ }
+ });
+ lines.push(line);
+ });
+
+ $('#<?=$id?>ExcelName').val("<?=$id?>");
+ $('#<?=$id?>ExcelHeading').val(JSON.stringify(heading));
+ $('#<?=$id?>ExcelLines').val(JSON.stringify(lines));
+ $('#<?=$id?>Excel').submit();
+ };
+
+<? if ($checkboxes) { ?>
+ $('#tableexcelsel-<?=$id?>').click(function () {
+ tableexcel(true);
+ });
+<? } ?>
+
+ $('#tableexcel-<?=$id?>').click(function () {
+ tableexcel(false);
+ });
+
+ $('#<?=$id?>').on('draw.dt', setupEditableChange);
+ setInterval(updateCount, 2000);
+
+ $('#<?=$id?> tbody').on('mouseenter', 'td', function () {
+ var cidx = dt.cell(this).index().column;
+ var ridx = dt.cell(this).index().row;
+
+ console.log(cidx);
+ console.log(ridx);
+
+ $(dt.cells().nodes()).removeClass('highlight');
+ $(dt.rows().nodes()).removeClass('highlight');
+
+ $(dt.column(cidx).nodes()).addClass('highlight');
+ $(dt.row(ridx).nodes()).addClass('highlight');
+ });
+ $('#<?=$id?> tbody').on('mouseleave', function () {
+ $(dt.cells().nodes()).removeClass('highlight');
+ $(dt.rows().nodes()).removeClass('highlight');
+ });
+ /* XXX:
+ * $('.dataTables_scrollBody').offset() .left .top
+ * $('.dataTables_scrollBody').scrollLeft(20); .scrollTop;
+ */
+
+ var setupAutolinks = function () {
+<?
+(function () use ($id, $noautolinks) {
+ if ($noautolinks) return;
+ $sel_id = $id;
+ include __DIR__ . "/auto_windowing.php";
+})();
+?>
+ };
+ $('#<?=$id?>').on('draw.dt', setupAutolinks);
+
+ })();
+
+ function getIDs_<?=$id?>() {
+ var ids = [];
+ var g = $.grep(autotables['<?=$id?>'].table().rows().nodes(), function (e) {
+ return e.classList.contains('selected');
+ });
+ $.each(g, function (i, e) {
+ var ee = false;
+ try { ee = e.getElementsByClassName('ajaxident')[0].getAttribute('data-id'); } catch (e) { e; }
+ if (ee) ids.push(ee);
+ else ids.push(e.getAttribute('data-id'));
+ });
+ return ids;
+ }
+
+ if ($('#insert_<?=$insert?>')) {
+ $('#insert_<?=$insert?>').replaceWith($('#autotable_containment_<?=$id?>'));
+ }
+
+ if (window.location !== window.parent.location) {
+ $('body').css('margin', '0');
+ var s = document.createElement('style');
+ s.type = "text/css";
+ s.innerHTML += '.dataTables_scrollBody { max-height: calc(100vh - 11em) !important; }';
+ document.getElementsByTagName('head')[0].appendChild(s);
+ }
+
+<? minEnd(); ?>
+ </script>
+
+<?
+ }
+
+ $num_rows = -1;
+ if (isset($r) && isset($r->num_rows))
+ $num_rows = $r->num_rows;
+ if (isset($stmt))
+ $stmt->reset();
+})();
+?>
diff --git a/autotable_explain.php b/autotable_explain.php
new file mode 100644
index 0000000..76dec14
--- /dev/null
+++ b/autotable_explain.php
@@ -0,0 +1,36 @@
+<?php
+/*
+ * 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.
+ */
+?>
+<div style="display: inline-block; align: right;">
+<? if (!$nofilter) { ?>
+ <span class="explain" onclick="$('#<?=$id?>_tablefilterinfo').toggle(); $(this).toggleClass('block');"><i class="fas fa-info-circle"></i> Filter</span>
+ <p id="<?=$id?>_tablefilterinfo" class="explain" style="display: none;">
+ Filtern nach "leer" mit <code class="explain">^$</code>, nach "nicht leer" mit <code class="explain">.+</code>.<br />
+ Filtern nach <u>exaktem</u> Inhalt: <code class="explain">^Maschinenbau$</code> (findet dann nicht mehr <code class="explain">"<span style="color: green;">Maschinenbau</span><span style="text-decoration: underline; color: red;"> (BCN)</span>"</code> usw.).<br />
+ Filtern am Anfang mit <code class="explain">^</code>, z.B. <code class="explain">^Jul</code>, am Ende mit <code class="explain">$</code>, z.B. <code class="explain">an FH$</code>.<br />
+ Filtern nach mehreren Kriterien gleichzeitig mit <code class="explain">|</code>, z.B. <code class="explain">Paderborn|Detmold</code>. LA Bachelor, aber nur Gy und BK, sowie LA Master alles: <code class="explain">(LA Bachelor (Gy|Ge))|(LA Master)</code>.<br />
+ Ranges: Anfangsbuchstaben A-K und P: <code class="explain">^[A-KP]</code>. Alles, was <i>keine</i> Umlaute hat: <code class="explain">^[^äöüÄÖÜ]*$</code>. Alles, was mind. einen Umlaut hat: <code class="explain">[äöüÄÖÜ]</code>.<br />
+ Beliebiges Zeichen am <code class="explain">.</code>: <code class="explain">Ni.las</code>. Alles mit exakt drei Zeichen: <code class="explain">^...$</code><br />
+ Repetition: Alles mit mind. zwei a im Namen: <code class="explain">a+</code>. Optionales u: <code class="explain">colou?r</code>. Null bis beliebig viele Zahlen hintereinander: <code class="explain">[0-9]*</code>.
+ </p>
+<? } ?>
+
+ <span class="explain" onclick="$('#<?=$id?>_tablecolumninfo').toggle(); $(this).toggleClass('block');"><i class="fas fa-info-circle"></i> Spalten</span>
+ <p id="<?=$id?>_tablecolumninfo" class="explain" style="display: none;">
+ Spalten können durch Klick auf die Spaltenüberschrift auf- (<i class="fas fa-sort-up"></i>) oder absteigend (<i class="fas fa-sort-down"></i>) sortiert werden.<br />
+ Zum Sortieren nach mehreren Kriterien gleichzeitig ab dem zweiten Kriterium <code class="explain">Umschalt/Shift</code> gedrückt halten beim Klicken der Spaltenüberschrift.<br />
+ Spalten können mit gedrückter Maustaste auf der Spaltenüberschrift beliebig verschoben werden.<br />
+ Ein- und Ausblenden von Spalten im Spaltenmenü (<i class="fas fa-columns"></i>).<br />
+ </p>
+</div>
+
diff --git a/autotable_fontsettings.php b/autotable_fontsettings.php
new file mode 100644
index 0000000..006b7d8
--- /dev/null
+++ b/autotable_fontsettings.php
@@ -0,0 +1,41 @@
+<?php
+/*
+ * 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.
+ */
+?>
+<span class="autotable_fontheight">
+ <i style="font-size: 8pt;" class="fas fa-font"></i><i class="fas fa-font"></i>
+ <select onchange="$('#<?=$id?>_wrapper').css('font-size', $(this).val()); $(window).resize();">
+ <option value=""></option>
+ <option value="8pt">8pt</option>
+ <option value="9pt">9pt</option>
+ <option value="10pt">10pt</option>
+ <option value="11pt">11pt</option>
+ <option value="12pt">12pt</option>
+ <option value="13pt">13pt</option>
+ <option value="14pt">14pt</option>
+ <option value="16pt">16pt</option>
+ <option value="18pt">18pt</option>
+ </select>
+</span>
+
+<span class="autotable_changefont">
+ <i class="fas fa-font"></i>
+ <select onchange="$('#<?=$id?>_wrapper').css('font-family', $(this).val()); $(window).resize();">
+ <option value=""></option>
+ <option style="font-family: Consolas !important" value="Consolas">Consolas</option>
+ <option style="font-family: 'Lucida Console' !important" value="Lucida Console">Lucida Console</option>
+ <option style="font-family: 'Lucida Sans' !important" value="Lucida Sans">Lucida Sans</option>
+ <option style="font-family: 'Courier New' !important" value="Courier New">Courier New</option>
+ <option style="font-family: 'Arial Narrow' !important" value="Arial Narrow">Arial Narrow</option>
+ <option style="font-family: Arial !important" value="Arial">Arial</option>
+ </select>
+</span>
diff --git a/calls.php b/calls.php
new file mode 100644
index 0000000..51ff544
--- /dev/null
+++ b/calls.php
@@ -0,0 +1,65 @@
+<?php
+/*
+ * 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.
+ */
+?>
+<?php
+/* vim: set ts=4 sw=4 et : */
+
+$_title = "Calls (Ausschreibungen)";
+
+require_once __DIR__ . "/check_auth.php";
+require_once __DIR__ . "/../includes/common.php";
+require_once __DIR__ . "/header.php";
+?>
+
+<h1><?=$_title?></h1>
+
+<?
+(function () use ($mysqli, $constraint, $nospinner, $noactions) {
+ if (!isset($noactions))
+ $noactions = false;
+
+ $sql = "
+SELECT * FROM calls
+";
+ $sql .= $constraint;
+ $stmt = $mysqli->prepare($sql);
+ $stmt->execute();
+ $r = $stmt->get_result();
+ $id = "calls";
+ $order = '[[2, "asc"], [8, "desc"]]';
+ $editable = [
+ 'app_start',
+ 'app_end',
+ 'default_valid_from',
+ 'default_valid_to'
+ ];
+ $ajax = false;
+ $idcell = "shorthand";
+ $entrytable = 'calls';
+ $nospinner = true;
+ include __DIR__ . '/autotable.php';
+ $stmt->reset();
+})();
+?>
+
+<h2>Aktuell für Bewerbungen zugeordnete Calls</h2>
+
+<ul>
+<li>
+DS: <?=$callsdefault['ds']['shorthand']?>
+<li>
+LÜ: <?=$callsdefault['lue']['shorthand']?>
+
+<?
+include_once __DIR__ . "/footer.php";
+?>
diff --git a/commission.php b/commission.php
new file mode 100644
index 0000000..ef410c9
--- /dev/null
+++ b/commission.php
@@ -0,0 +1,74 @@
+<?php
+/*
+ * 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.
+ */
+?>
+<?php
+/* vim: set ts=4 sw=4 et : */
+
+$_title = "Alte Kommissionsmitglieder, obsolet <s>Alle aktuellen Kommissionsmitglieder</s>";
+
+require_once __DIR__ . "/check_auth.php";
+require_once __DIR__ . "/../includes/common.php";
+include_once __DIR__ . "/header.php";
+require_once __DIR__ . "/lookup.php";
+
+doTitle();
+
+(function () use ($mysqli) {
+ $sql = "
+SELECT
+ Kommissionsmitglieder.ID, Kommissionsmitglieder.Person AS PersID, Personen.Nachname, Personen.Vorname,
+ Hochschulen.KanonischerName AS Hochschule, Kommissionsmitglieder.Fakultät AS Fakultaet,
+ Kommissionsmitglieder.DezentraleKommission, Kommissionsmitglieder.ZentraleKommission, Kommissionsmitglieder.Sozialkommission,
+ Kommissionsmitglieder.Kommission,
+ Personen.Email, Personen.Straße, Personen.PLZ, Personen.Ort, Personen.Adresszusatz,
+ Kommissionen.Benutzername AS Kurzname, Kommissionen.Passwort, Kommissionen.`Webinterface Link` AS Onlineraum, Personen.Anrede,
+ Personen.Ansprache, Personen.Titel, Kommissionsmitglieder.Fakultät
+FROM Kommissionsmitglieder
+LEFT JOIN Personen ON Kommissionsmitglieder.Person = Personen.ID
+LEFT JOIN Kommissionen ON Kommissionsmitglieder.Kommission = Kommissionen.ID
+LEFT JOIN Hochschulen ON Kommissionsmitglieder.Hochschule = Hochschulen.ID
+WHERE ((((Kommissionsmitglieder.Person)=Personen.ID) AND ((Kommissionsmitglieder.DezentraleKommission)=True)) OR (((Kommissionsmitglieder.ZentraleKommission)=True)))
+";
+ $stmt = $mysqli->prepare($sql);
+ $stmt->execute();
+ $r = $stmt->get_result();
+ $id = "commission";
+ $order = "[[3, 'asc'], [4, 'asc']]";
+ $checkboxes = true;
+ include __DIR__ . '/autotable.php';
+ $stmt->reset();
+
+ $email_sql = $sql . ' AND Kommissionsmitglieder.ID IN ';
+ $payload_sql = "SELECT Personen.ID AS PersID, event_participants.token, event_participants.id AS PartID, events.* FROM event_participants INNER JOIN Personen ON event_participants.persid=Personen.ID INNER JOIN events ON event_participants.eventid = events.id WHERE events.id = 78 OR Personen.mpulsid IN ";
+ $independent_payload_sql = "
+SELECT
+Stipendien.*, Personen.*,
+
+ds.kommission, ds.bewertung, ds.foerderlimit, ds.rang,
+leistung.commission
+
+FROM Stipendien
+
+LEFT JOIN ds ON Stipendien.mpulsid = ds.uid
+LEFT JOIN leistung ON Stipendien.mpulsid = leistung.uid
+
+INNER JOIN Personen ON Stipendien.Person = Personen.ID
+
+WHERE Stipendien.Jahr = 2020 AND Stipendien.Förderbeginn >= '2020-10-01'
+";
+ include __DIR__ . '/autoactions.php';
+ include __DIR__ . '/autoemail.php';
+ include __DIR__ . '/autoevent.php';
+})();
+
+include_once __DIR__ . "/footer.php";
diff --git a/commissioners.php b/commissioners.php
new file mode 100644
index 0000000..ac389ca
--- /dev/null
+++ b/commissioners.php
@@ -0,0 +1,161 @@
+<?php
+/*
+ * 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.
+ */
+?>
+<?php
+/* vim: set ts=4 sw=4 et : */
+
+$_title = "Kommissionsmitglieder: mit Zuordnung";
+
+require_once __DIR__ . "/check_auth.php";
+require_once __DIR__ . "/../includes/common.php";
+include_once __DIR__ . "/header.php";
+require_once __DIR__ . "/lookup.php";
+
+doTitle();
+
+(function () use ($kommissionen, $kommissionen_sozial, $calls) {
+ $sql = "
+SELECT
+ commissioners.ID,
+ Personen.ID AS PersID,
+ Personen.Nachname,
+ Personen.Vorname,
+ Personen.Email,
+ `call` AS `call`,
+ Kommissionen.ID AS Kommission,
+ `call` AS `CallText`,
+ Kommissionen.Benutzername AS Shorthand,
+ Personen.Anrede,
+ Personen.Ansprache,
+ Personen.`informale Ansprache`,
+ Personen.Titel,
+ Personen.Straße,
+ Personen.Adresszusatz,
+ Personen.PLZ,
+ Personen.Ort
+
+FROM commissioners
+
+LEFT JOIN Personen ON commissioners.Person = Personen.ID
+LEFT JOIN Kommissionen ON commissioners.Kommission = Kommissionen.ID
+
+";
+ $id = "commissioners_new";
+ $getthdef = true;
+ $order = '[[1, "desc"]]';
+ $types = [
+ 'Kommission' => ['select', $kommissionen],
+ 'call' => ['', $calls]
+ ];
+ $editable = [
+ 'call',
+ 'Kommission'
+ ];
+ $entrytable = 'commissioners';
+
+ $bottom = <<<EOD
+<div class="admin_actionsbelow">
+ Ausgewählte
+ <button class="medium" style="background: darkorange;" onclick="return delEntry();"><i class="far fa-trash-alt"></i> endgültig löschen</button>
+</div>
+
+<button class="medium" onclick="return addEntry();"><i class="fas fa-user-tie"></i> neuen Kommissionsmitgliedeintrag anlegen</button>
+EOD;
+ $alwaysbottom = true;
+
+ $pdf_sql = $email_sql = "
+SELECT
+CONCAT(commissioners.ID, 'commissioner', '-', Personen.ID, 'person') AS uid,
+Personen.ID AS PersID,
+Personen.*,
+`call` AS `call`,
+Kommissionen.Darstellungsname AS Kommission,
+Kommissionen.`Webinterface Link` AS Link,
+Kommissionen.`LinkLeistungsueberpruefung` AS LUeLink,
+`call` AS `CallText`,
+Kommissionen.Benutzername AS Shorthand
+
+FROM commissioners
+LEFT JOIN Personen ON commissioners.Person = Personen.ID
+LEFT JOIN Kommissionen ON commissioners.Kommission = Kommissionen.ID
+WHERE commissioners.ID IN
+";
+ $independent_payload_sql = "SELECT Stipendien.*, Personen.*, leistung.commission FROM Stipendien INNER JOIN leistung ON Stipendien.mpulsid = leistung.uid INNER JOIN Personen ON Stipendien.Person = Personen.ID WHERE Stipendien.Jahr = 2022 AND Stipendien.Förderart=7 AND Stipendien.Förderbeginn >= '2022-10-01'";
+
+ $payload_sql = "
+SELECT eventid, persid, token FROM event_participants WHERE eventid=647 AND '999999999' NOT IN
+";
+
+ $independent_payload_sql = "
+SELECT
+
+ Stipendien.*, Personen.*,
+ ds.kommission AS komm_ds, ds.bewertung, ds.foerderlimit, ds.rang,
+ leistung.commission AS komm_leistung,
+ social.commission AS komm_sozial
+
+FROM Stipendien
+
+LEFT JOIN ds ON Stipendien.mpulsid = ds.uid
+LEFT JOIN leistung ON Stipendien.mpulsid = leistung.uid
+LEFT JOIN social ON Stipendien.mpulsid = social.commission
+
+INNER JOIN Personen ON Stipendien.Person = Personen.ID
+
+WHERE Stipendien.Jahr = 2023 AND Stipendien.Förderbeginn >= '2023-10-01'
+";
+
+ $checkboxes = true;
+ include __DIR__ . '/autotable.php';
+ include __DIR__ . '/autoactions.php';
+ include __DIR__ . '/autoemail.php';
+ include __DIR__ . '/autopdf.php';
+ include __DIR__ . '/autoevent.php';
+})();
+?>
+
+<script>
+<? minStart(); ?>
+function delEntry() {
+ $.ajaxSetup({async:false});
+ $.ajax({
+ type: 'POST',
+ url: '/db/main/delcomm.php',
+ data: {
+ 'ids': getIDs_commissioners_new()
+ }
+ });
+ $.ajaxSetup({async:true});
+ location.href = location.href;
+}
+
+function addEntry() {
+ promptPerson('', function (pid) {
+ if (!pid) return;
+ $.ajaxSetup({async:false});
+ $.ajax({
+ type: 'POST',
+ url: '/db/main/addcomm.php',
+ data: {
+ 'persid': pid
+ }
+ });
+ $.ajaxSetup({async:true});
+ location.href = location.href;
+ });
+}
+<? minEnd(); ?>
+</script>
+
+<?
+include_once __DIR__ . "/footer.php";
diff --git a/commissioners_person.php b/commissioners_person.php
new file mode 100644
index 0000000..2e49cb9
--- /dev/null
+++ b/commissioners_person.php
@@ -0,0 +1,113 @@
+<?php
+/*
+ * 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.
+ */
+?>
+<?php
+/* vim: set ts=4 sw=4 et : */
+
+$_title = "Kommissionsmitglieder: nur Person <small>(neues Datenmodell)</small>";
+
+require_once __DIR__ . "/check_auth.php";
+require_once __DIR__ . "/../includes/common.php";
+include_once __DIR__ . "/header.php";
+require_once __DIR__ . "/lookup.php";
+
+doTitle();
+
+(function () use ($kommissionen, $calls) {
+ $sql = "
+SELECT DISTINCT
+ Personen.ID AS PersID,
+ Personen.Nachname,
+ Personen.Vorname,
+ Personen.Email,
+ Personen.Straße,
+ Personen.Adresszusatz,
+ Personen.PLZ,
+ Personen.Ort,
+ GROUP_CONCAT(Hochschulen.KanonischerName SEPARATOR ', ') AS `Hochschule(n)`,
+ GROUP_CONCAT(Kommissionen.Darstellungsname SEPARATOR ', ') AS `Kommission(en) Name`,
+ GROUP_CONCAT(commissioners.`call` SEPARATOR ', ') AS `Calls`,
+ Personen.Anrede,
+ Personen.Ansprache,
+ Personen.`informale Ansprache`,
+ Personen.Titel
+
+FROM commissioners
+
+LEFT JOIN Personen ON commissioners.Person = Personen.ID
+LEFT JOIN Kommissionen ON commissioners.Kommission = Kommissionen.ID
+LEFT JOIN Hochschulen ON Kommissionen.Hochschule = Hochschulen.ID
+
+GROUP BY Personen.ID
+
+";
+ $id = "commissioners_new3";
+ $getthdef = true;
+ $order = '[[1, "desc"]]';
+ $entrytable = 'Personen';
+ $idcell = "PersID";
+ $idcellreal = "ID";
+ $editable = ['Nachname', 'Vorname', 'Email', 'Straße', 'Adresszusatz', 'PLZ', 'Ort', 'Anrede', 'Ansprache', 'informale Ansprache', 'Titel'];
+
+ $email_sql = "
+SELECT DISTINCT CONCAT(Personen.ID, 'person') AS uid, Personen.*, Personen.Straße AS Strasse, Personen.ID AS PersID
+FROM Personen
+WHERE Personen.ID IN
+";
+ $ajax = false;
+ $pdf_sql = $email_sql;
+ $checkboxes = true;
+ include __DIR__ . '/autotable.php';
+ include __DIR__ . '/autoactions.php';
+ include __DIR__ . '/autoemail.php';
+ include __DIR__ . '/autopdf.php';
+ include __DIR__ . '/autoevent.php';
+})();
+?>
+
+<script>
+<? minStart(); ?>
+function delEntry() {
+ $.ajaxSetup({async:false});
+ $.ajax({
+ type: 'POST',
+ url: '/db/main/delcomm.php',
+ data: {
+ 'ids': getIDs_commissioners_new()
+ }
+ });
+ $.ajaxSetup({async:true});
+ location.href = location.href;
+}
+
+function addEntry() {
+ promptPerson('', function (pid) {
+ if (!pid) return;
+ $.ajaxSetup({async:false});
+ $.ajax({
+ type: 'POST',
+ url: '/db/main/addcomm.php',
+ data: {
+ 'persid': pid
+ }
+ });
+ $.ajaxSetup({async:true});
+ location.href = location.href;
+ });
+}
+<? minEnd(); ?>
+</script>
+
+<?
+include_once __DIR__ . "/footer.php";
+?>
diff --git a/commissions.php b/commissions.php
new file mode 100644
index 0000000..0c6f754
--- /dev/null
+++ b/commissions.php
@@ -0,0 +1,61 @@
+<?php
+/*
+ * 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.
+ */
+?>
+<?php
+/* vim: set ts=4 sw=4 et : */
+
+$_title = "Alle Kommissionen";
+
+require_once __DIR__ . "/check_auth.php";
+require_once __DIR__ . "/../includes/common.php";
+include_once __DIR__ . "/header.php";
+require_once __DIR__ . "/lookup.php";
+?>
+
+<h1><?=$_title?></h1>
+
+<?php
+(function () use ($mysqli) {
+ $sql = "
+SELECT
+ Kommissionen.ID,
+ Benutzername AS Shorthand,
+ Name,
+ Quote,
+ sum(if(Stipendien.Jahr=2024 AND leistung.uid, 1, 0)) AS LÜler,
+ sum(if(Stipendien.Jahr=2024 AND leistung.pass=1, 1, 0)) AS LÜBestanden,
+ sum(if(Stipendien.Jahr=2024 AND leistung.accepted=1, 1, 0)) AS LÜBestAngenommen,
+ sum(if(Stipendien.Jahr=2024 AND leistung.accepted=0, 1, 0)) AS LÜBestAbgelehnt
+
+FROM Kommissionen
+LEFT JOIN leistung ON Kommissionen.Benutzername = leistung.commission
+LEFT JOIN Stipendien ON leistung.uid = Stipendien.ID
+GROUP BY Kommissionen.Benutzername
+";
+ $stmt = $mysqli->prepare($sql);
+ $stmt->execute();
+ $r = $stmt->get_result();
+ $id = "commissions";
+ $order = '[[2, "asc"]]';
+ $entrytable = 'Kommissionen';
+ $editable = [
+ 'Name',
+ 'Quote',
+ ];
+ $links = [
+ ];
+ include __DIR__ . '/autotable.php';
+ $stmt->reset();
+})();
+
+include_once __DIR__ . "/footer.php";
diff --git a/composer.json b/composer.json
new file mode 100644
index 0000000..f30327f
--- /dev/null
+++ b/composer.json
@@ -0,0 +1,9 @@
+{
+ "require": {
+ "twig/twig": "<3.0",
+ "mpdf/mpdf": ">=7.1",
+ "phpoffice/phpspreadsheet": ">=1.6",
+ "matthiasmullie/minify": ">=1.3",
+ "symfony/http-foundation": "^5.4"
+ }
+}
diff --git a/contract.php b/contract.php
new file mode 100644
index 0000000..e180142
--- /dev/null
+++ b/contract.php
@@ -0,0 +1,111 @@
+<?php
+/*
+ * 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.
+ */
+?>
+<?php
+/* vim: set ts=4 sw=4 et : */
+
+require_once __DIR__ . "/check_auth.php";
+require_once __DIR__ . "/../includes/common.php";
+require_once __DIR__ . "/lookup.php";
+require_once __DIR__ . "/lookup_more.php";
+
+(function () use ($mysqli) {
+ global $foerdID, $persID, $orgaID, $persXorga;
+
+ $sql = "
+SELECT
+ contracts.*,
+ contracts.contact AS APID,
+ Förderer.*,
+ Förderer.ID AS FoerdID,
+ Organisationen.Name AS Förderer,
+ Organisationen.ID AS OrgaID,
+ contracts.id AS ID,
+ ((150 * contracts.ss_months) + (150 * contracts.ls_months)) AS Summe,
+ CONCAT(IF(Förderer.`SEPA-Lastschrift erteilt`, 'SEPA|', ''), IF(SUM(Spenden.Betrag) > 0, SUM(Spenden.Betrag), 0)) AS Eingang,
+ IF(Förderer.`SEPA-Lastschrift erteilt`, 0, ((150 * contracts.ss_months) + (150 * contracts.ls_months)) - IF(SUM(Spenden.Betrag) > 0, SUM(Spenden.Betrag), 0)) AS Fehlbetrag
+FROM contracts
+LEFT JOIN Förderer ON contracts.patron = Förderer.ID
+LEFT JOIN Organisationen ON Förderer.Organisation = Organisationen.ID
+LEFT JOIN Spenden ON contracts.id = Spenden.contract
+WHERE contracts.id=?
+";
+ $stmt = $mysqli->prepare($sql);
+ $stmt->bind_param('i', $_GET['id']);
+ $stmt->execute();
+ $r = $stmt->get_result();
+ $l = $r->fetch_object();
+ $foerdID = $l->FoerdID;
+ $persID = $l->PersID;
+ $orgaID = $l->OrgaID;
+
+ $_title = 'Vertrag (ID ' . $_GET['id'] . ')' . ' zu: <span class="pii">' . $l->Förderer . '</span>';
+ include_once __DIR__ . "/header.php";
+?>
+
+<h1 style="display: inline-block;"><?=$_title?></h1>
+&nbsp;<button class="small" onclick="showNewNoteDialog({'foerdid': <?=$foerdID?>, 'orgaid': <?=$orgaID?>});"><i class="fas fa-sticky-note"></i> Notiz anlegen</button>
+&nbsp;<button class="small" style="background-color: red;" onclick="location.href='/db/main/delcontractc.php?id=<?=$l->ID?>';"><i class="fas fa-trash-alt"></i></sup> Datensatz löschen</button>
+
+<?php
+ $sql = "SELECT Personen.ID, Personen.Nachname, Personen.Vorname FROM Personen_Organisationen INNER JOIN Personen ON Personen_Organisationen.Person = Personen.ID WHERE Personen_Organisationen.Organisation = ? ORDER BY Personen.Nachname";
+ $stmt = $mysqli->prepare($sql);
+ $stmt->bind_param('i', $orgaID);
+ $stmt->bind_result($id, $nachname, $vorname);
+ $stmt->execute();
+ $persXorga = [];
+ while ($stmt->fetch()) {
+ $persXorga[$id] = $nachname . ", " . $vorname;
+ }
+ $stmt->reset();
+
+ $id = "contract";
+ $entrytable = "contracts";
+ $def = [
+ "Förderer",
+ [10, '_FoerdID', '_=3Förderer', '_=3legal|rechtsformen~Rechtsform', '_=3Zuständig|demo'],
+ "Vertrag",
+ [10, '=2call#string|callsByID', 'valid_from#isodate~gültig von', 'valid_to#isodate~gültig bis', '=2ls#number~Anzahl Leistungsstipendien', 'ls_months#number~Monate', '=2ss#number~Anzahl Sozialstipendien', 'ss_months#number~Monate'],
+ [10, '_APID', '=5contact|persXorga~Ansprechpartner', '_Summe', '_Eingang', '_=2Fehlbetrag'],
+ [10, '=9remark~Anmerkung', '+=1kontrolliert'],
+ ['*widmung'],
+ ];
+
+ include __DIR__ . '/autoform.php';
+})();
+
+(function () {
+ $title = "Spenden zum Vertrag";
+ $id = "donationsXcontract2";
+ $sql = "
+SELECT
+ Spenden.ID AS SpendenID,
+ Spenden.Person AS PersID,
+ Personen.Nachname,
+ Personen.Vorname,
+ Spenden.Organisation AS OrgaID,
+ Organisationen.Name AS Organisation,
+ Spenden.Betrag,
+ DATE(Spenden.Geldeingang) AS Geldeingang
+FROM Spenden
+LEFT JOIN Personen ON Spenden.Person = Personen.ID
+LEFT JOIN Organisationen ON Spenden.Organisation = Organisationen.ID
+WHERE Spenden.contract=" . $_GET['id'];
+
+ $thdef = ['SpendenID', 'PersID', 'Nachname', 'Vorname', 'OrgaID', 'Organisation', 'Betrag', 'Geldeingang'];
+ $order = '[[1, "desc"]]';
+ $ajax = $nospinner = true;
+ include __DIR__ . '/autotable.php';
+})();
+
+include_once __DIR__ . '/footer.php';
diff --git a/contracts.php b/contracts.php
new file mode 100644
index 0000000..a9d4d53
--- /dev/null
+++ b/contracts.php
@@ -0,0 +1,19 @@
+<?php
+/*
+ * 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.
+ */
+?>
+<?php
+/* vim: set ts=4 sw=4 et : */
+
+$_title = "Alle Verträge";
+$_constraint = "";
+require_once __DIR__ . "/contracts_common.php";
diff --git a/contracts_common.php b/contracts_common.php
new file mode 100644
index 0000000..84b68ff
--- /dev/null
+++ b/contracts_common.php
@@ -0,0 +1,146 @@
+<?php
+/*
+ * 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.
+ */
+?>
+<?php
+/* vim: set ts=4 sw=4 et : */
+
+require_once __DIR__ . "/check_auth.php";
+require_once __DIR__ . "/../includes/common.php";
+include_once __DIR__ . "/header.php";
+require_once __DIR__ . "/lookup.php";
+
+doTitle();
+
+if (isset($_GET['apfi']) && $_GET['apfi'] == 1) {
+?>
+ <p>Der/Die Ansprechpartner Finanzen (apfi) werden für den E-Mail-Versand benutzt. <a href="?apfi=0">Auf AP Zuwendungen zurückschalten.</a></p>
+<p>Der Ansprechpartner Zuwendungen ist Adressat des PDF-Dokuments.</p>
+<? } else { ?>
+ <p>Der Ansprechpartner Zuwendungen des Förderers wird für den E-Mail-Versand benutzt. <a href="?apfi=1">Auf AP Finanzen schalten.</a></p>
+ <p>Der Ansprechpartner Zuwendungen ist Adressat des PDF-Dokuments.</p>
+<?
+}
+
+(function () use ($mysqli, $_constraint) {
+ $sql = "
+SELECT
+ contracts.id AS VertrID, CONCAT(calls.name, ' [', `call`, ']') as 'Call', patron AS FoerdID, Organisationen.Name AS Förderer,
+ Personen.Nachname AS Zuständig,
+ ls AS Stipendien,
+ contracts.remark,
+ CONCAT(IF(Förderer.`SEPA-Lastschrift erteilt`, 'SEPA|', ''), IF(SUM(Spenden.Betrag) > 0, SUM(Spenden.Betrag), 0)) AS Eingang,
+ IF(Förderer.`SEPA-Lastschrift erteilt`, 0, ((150 * contracts.ss + 150 * contracts.ls) * 12) - IF(SUM(Spenden.Betrag) > 0, SUM(Spenden.Betrag), 0)) AS Fehlbetrag,
+ GROUP_CONCAT(DATE(Spenden.Geldeingang) SEPARATOR ', ') AS Geldeingänge,
+ CONCAT(DATE(contracts.valid_from), ' bis ', DATE(contracts.valid_to)) AS 'Gültigkeit',
+ DATE(Förderer.`Vertrag läuft aus`) AS 'allgemeiner Auslauf',
+ contracts.kontrolliert,
+ `Zahlungsaufforderung gewünscht`
+FROM contracts
+LEFT JOIN Förderer ON contracts.patron = Förderer.ID
+LEFT JOIN Organisationen ON Förderer.Organisation = Organisationen.ID
+LEFT JOIN Spenden ON contracts.id = Spenden.contract
+LEFT JOIN calls ON contracts.`call` = calls.shorthand
+LEFT JOIN Personen ON Förderer.Zuständig = Personen.ID
+GROUP BY contracts.id
+";
+ $sql .= $_constraint;
+ $stmt = $mysqli->prepare($sql);
+ $stmt->execute();
+ $r = $stmt->get_result();
+ $id = "contractscommon4";
+ $idcell = "VertrID";
+ $idcellreal = "id";
+ $order = '[[4, "asc"]]';
+ $entrytable = 'contracts';
+ $types = [
+ 'kontrolliert' => 'checkbox',
+ ];
+ $editable = [
+ 'kontrolliert',
+ 'remark',
+ ];
+ $checkboxes = true;
+ $bottom = <<<EOD
+ <div class="admin_actionsbelow">
+ Ausgewählte
+ <button class="medium" style="background: darkorange;" onclick="return delContract();"><i class="fas fa-trash-alt"></i> löschen</button>
+ </div>
+EOD;
+ $email_sql = "
+SELECT Förderer.ID AS FoerdID, Organisationen.Name AS Foerderer, CONCAT(Förderer.ID, 'patron', '-', Personen.ID, 'person', '-', contracts.id, 'contract') AS uid, Personen.*, Organisationen.strasse AS Strasse, Organisationen.PLZ as PLZ, Organisationen.Ort AS Ort, Organisationen.Adresszusatz as Adresszusatz, Organisationen.Name AS Organisation, CONCAT(Förderer.ID, 'patron', '-', Personen.ID, 'person', '-', contracts.id, 'contract') AS genuid,
+
+ls AS Leistung, ss AS Sozial, (ls * 150 * 12 + ss * 150 * 12) AS Summe,
+IF(SUM(Spenden.Betrag) > 0, SUM(Spenden.Betrag), 0) AS Eingang,
+((150 * contracts.ss + 150 * contracts.ls) * 12) - IF(SUM(Spenden.Betrag) > 0, SUM(Spenden.Betrag), 0) AS Fehlbetrag
+
+FROM contracts
+
+INNER JOIN Förderer ON contracts.patron = Förderer.ID
+LEFT JOIN Organisationen ON Förderer.Organisation = Organisationen.ID
+LEFT JOIN Personen ON Förderer.zuwendungen = Personen.ID
+LEFT JOIN Spenden ON contracts.id = Spenden.contract
+
+WHERE contracts.id IN ";
+
+ $email_sql_apfi = "
+SELECT Förderer.ID AS FoerdID, Organisationen.Name AS Foerderer, CONCAT(Förderer.ID, 'patron', '-', Personen2.ID, 'person', '-', contracts.id, 'contract') AS uid, Personen.*, Organisationen.strasse AS Strasse, Organisationen.PLZ as PLZ, Organisationen.Ort AS Ort, Organisationen.Adresszusatz as Adresszusatz, Organisationen.Name AS Organisation, CONCAT(Förderer.ID, 'patron', '-', Personen2.ID, 'person', '-', contracts.id, 'contract') AS genuid,
+
+ls AS Leistung, ss AS Sozial, (ls * 150 * 12 + ss * 150 * 12) AS Summe,
+IF(SUM(Spenden.Betrag) > 0, SUM(Spenden.Betrag), 0) AS Eingang,
+((150 * contracts.ss + 150 * contracts.ls) * 12) - IF(SUM(Spenden.Betrag) > 0, SUM(Spenden.Betrag), 0) AS Fehlbetrag
+
+FROM contracts
+
+INNER JOIN Förderer ON contracts.patron = Förderer.ID
+INNER JOIN Organisationen ON Förderer.Organisation = Organisationen.ID
+
+LEFT JOIN Personen_Organisationen ON Organisationen.ID = Personen_Organisationen.Organisation
+LEFT JOIN Personen ON Personen_Organisationen.Person = Personen.ID
+LEFT JOIN Personen AS Personen2 ON Förderer.zuwendungen = Personen2.ID
+LEFT JOIN Spenden ON contracts.id = Spenden.contract
+
+WHERE Personen_Organisationen.apfi = 1
+AND contracts.id IN ";
+
+ $pdf_sql = $email_sql;
+ if (isset($_GET['apfi']) && $_GET['apfi'] == 1) $email_sql = $email_sql_apfi;
+ $post_sql = ' GROUP BY contracts.id, Personen.ID';
+ $exactmatch = 1;
+
+ include __DIR__ . '/autotable.php';
+ include __DIR__ . '/autoactions.php';
+ include __DIR__ . '/autoemail.php';
+ include __DIR__ . '/autopdf.php';
+ $stmt->reset();
+})();
+?>
+
+<script>
+function delContract() {
+ $.ajaxSetup({async:false});
+ $.ajax({
+ type: 'POST',
+ url: '/db/main/delcontract.php',
+ data: {
+ 'ids': getIDs_contractscommon3()
+ }
+ });
+ $.ajaxSetup({async:true});
+ location.reload();
+ return false;
+}
+</script>
+
+<?php
+include_once __DIR__ . "/footer.php";
+?>
diff --git a/curfutpatrons.php b/curfutpatrons.php
new file mode 100644
index 0000000..de9642a
--- /dev/null
+++ b/curfutpatrons.php
@@ -0,0 +1,19 @@
+<?php
+/*
+ * 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.
+ */
+?>
+<?php
+/* vim: set ts=4 sw=4 et : */
+
+$_title = "Aktuelle und künftige Förderer <small>(anhand Vertr&auml;ge)</small>";
+$_constraint = "WHERE (contracts.valid_to >= NOW() OR contracts.valid_to IS NULL) AND contracts.id > 0 GROUP BY Förderer.ID";
+require_once __DIR__ . "/patrons_common.php";
diff --git a/curfutpatronspersons.php b/curfutpatronspersons.php
new file mode 100644
index 0000000..c075562
--- /dev/null
+++ b/curfutpatronspersons.php
@@ -0,0 +1,27 @@
+<?php
+/*
+ * 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.
+ */
+?>
+<?php
+/* vim: set ts=4 sw=4 et : */
+
+$_title = "Alle Personen bei aktuellen und künftigen Förderern <small>(anhand Vertr&auml;ge)</small>";
+
+$call = '%';
+if (isset($_GET['call']) && $_GET['call'] != '') {
+ $call = filter_var($_GET['call'], FILTER_SANITIZE_STRING);
+ $_title .= " <small>(Call " . $call . ")</small>";
+}
+
+$_constraint = "WHERE Förderer.ID IN (SELECT contracts.patron FROM contracts WHERE (contracts.valid_to >= NOW()) AND (contracts.`call` LIKE '" . $call . "'))";
+
+require __DIR__ . "/patronspersons_common.php";
diff --git a/curpatrons.php b/curpatrons.php
new file mode 100644
index 0000000..e7afd1c
--- /dev/null
+++ b/curpatrons.php
@@ -0,0 +1,19 @@
+<?php
+/*
+ * 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.
+ */
+?>
+<?php
+/* vim: set ts=4 sw=4 et : */
+
+$_title = "Aktuelle Förderer <small>(altes Datenmodell, anhand Häkchen)</small>";
+$_constraint = "WHERE Förderer.`aktuell Förderer` = 1";
+require_once __DIR__ . "/patrons_common.php";
diff --git a/curstips.php b/curstips.php
new file mode 100644
index 0000000..7a46376
--- /dev/null
+++ b/curstips.php
@@ -0,0 +1,19 @@
+<?php
+/*
+ * 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.
+ */
+?>
+<?php
+/* vim: set ts=4 sw=4 et : */
+
+$_title = "Aktuelle Stipendien";
+$_constraint = "WHERE Stipendien.Förderende >= NOW() AND Stipendien.Förderbeginn <= NOW()";
+require_once __DIR__ . '/stips_common.php';
diff --git a/curstipspers.php b/curstipspers.php
new file mode 100644
index 0000000..57728e8
--- /dev/null
+++ b/curstipspers.php
@@ -0,0 +1,58 @@
+<?php
+/*
+ * 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.
+ */
+?>
+<?php
+/* vim: set ts=4 sw=4 et : */
+
+$_title = "Alle aktuellen und zukünftigen Stipendiatinnen und Stipendiaten (nur Person)";
+
+require_once __DIR__ . "/check_auth.php";
+include_once __DIR__ . "/header.php";
+require_once __DIR__ . "/../includes/common.php";
+require_once __DIR__ . "/lookup.php";
+
+doTitle();
+
+(function () use ($mysqli) {
+ $sql = "
+SELECT
+ Personen.ID AS PersID,
+ Personen.Nachname, Personen.Vorname,
+ Personen.Geschlecht,
+ Personen.Ort,
+ Personen.Email,
+ Personen.Telefon,
+ Personen.Handy
+
+FROM Stipendien
+LEFT JOIN Personen ON Stipendien.Person = Personen.ID
+WHERE Stipendien.Förderende > NOW()
+GROUP BY Personen.ID
+";
+ $stmt = $mysqli->prepare($sql);
+ $stmt->execute();
+ $r = $stmt->get_result();
+ $id = "curfutpersons";
+ $order = '[[2, "asc"], [3, "asc"]]';
+ $checkboxes = true;
+ include __DIR__ . '/autotable.php';
+ $stmt->reset();
+
+ $pdf_sql = $email_sql = "SELECT Personen.*, Personen.ID AS PersID FROM Personen WHERE ID IN ";
+ include __DIR__ . '/autoactions.php';
+ include __DIR__ . '/autoemail.php';
+ include __DIR__ . '/autopdf.php';
+ include __DIR__ . '/autoevent.php';
+})();
+
+include_once __DIR__ . "/footer.php";
diff --git a/delay.php b/delay.php
new file mode 100644
index 0000000..ca28a00
--- /dev/null
+++ b/delay.php
@@ -0,0 +1,22 @@
+<?php
+/*
+ * 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.
+ */
+?>
+<?php
+/* vim: set ts=4 sw=4 et : */
+
+if (isset($_POST['time']) && $_POST['time'] > 1)
+ usleep($_POST['time']*1000);
+else
+ usleep(60*1000);
+echo "true";
+exit(0);
diff --git a/delcomm.php b/delcomm.php
new file mode 100644
index 0000000..611ba31
--- /dev/null
+++ b/delcomm.php
@@ -0,0 +1,20 @@
+<?php
+/*
+ * 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.
+ */
+?>
+<?php
+/* vim: set ts=4 sw=4 et : */
+
+require_once __DIR__ . "/check_auth.php";
+require_once __DIR__ . "/../includes/common.php";
+
+deleteSimple("DELETE FROM commissioners WHERE id IN");
diff --git a/delcontract.php b/delcontract.php
new file mode 100644
index 0000000..e062038
--- /dev/null
+++ b/delcontract.php
@@ -0,0 +1,20 @@
+<?php
+/*
+ * 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.
+ */
+?>
+<?php
+/* vim: set ts=4 sw=4 et : */
+
+require_once __DIR__ . "/check_auth.php";
+require_once __DIR__ . "/../includes/common.php";
+
+deleteSimple("DELETE FROM contracts WHERE id IN");
diff --git a/delcontractc.php b/delcontractc.php
new file mode 100644
index 0000000..e7ad4b5
--- /dev/null
+++ b/delcontractc.php
@@ -0,0 +1,140 @@
+<?php
+/*
+ * 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.
+ */
+?>
+<?
+/* vim: set ts=4 sw=4 et : */
+
+require_once __DIR__ . "/check_auth.php";
+require_once __DIR__ . "/../includes/common.php";
+
+$contractID = $_GET['id'];
+$prohibit = false;
+
+(function () use ($mysqli, $_title, $contractID) {
+ $sql = "
+SELECT contracts.id, contracts.`call`, Organisationen.Name
+FROM contracts
+LEFT JOIN Förderer ON contracts.patron=Förderer.ID
+LEFT JOIN Organisationen ON Förderer.Organisation = Organisationen.ID
+WHERE contracts.id=?;";
+ $stmt = $mysqli->prepare($sql);
+ $stmt->bind_param('i', $contractID);
+ $stmt->execute();
+ $r = $stmt->get_result();
+ $l = $r->fetch_object();
+
+ $_title = '<span class="pii">' . $l->Name . ', ' . $l->call . '</span> (ID ' . $contractID . ')';
+ include_once __DIR__ . "/header.php";
+?>
+ <h1 style="display: inline-block;"><span style="color: red; font-weight: bold;">Vertragsdatensatz löschen:</span> <?=$_title?></h1>
+<?
+ if ($l->id < 1) {
+?>
+ <p><b>Kein Datensatz mit dieser ID gefunden!</b></p>
+ <p>Wenn Sie gerade gelöscht haben, war das Löschen erfolgreich.</p>
+
+<? if (isset($_GET['autodel']) && $_GET['autodel'] == 1) { ?>
+ <script>
+ window.close();
+ </script>
+<? }
+ exit(0);
+ }
+
+ $def = [
+ "Auszug",
+ [6, '_=4Förderer', '_Call']
+ ];
+ include __DIR__ . '/autoform.php';
+ $stmt->reset();
+})();
+
+(function () use ($contractID) {
+ $globstring = "{" . $contractID . "contract}_";
+ require_once __DIR__ . "/doc.php";
+})();
+
+(function () use ($mysqli, $contractID, &$num_rows) {
+ $title = "Spenden";
+ $nochosen = $nofilter = true;
+ $sql = "
+SELECT
+ Spenden.ID AS SpendenID,
+ Spenden.Organisation AS OrgaID,
+ Organisationen.Name AS Organisation,
+ CONCAT(calls.name, ' [', contracts.call, '] - ', ContractOrga.Name) AS Vertrag,
+ Betrag,
+ DATE(Geldeingang) AS Geldeingang
+FROM Spenden
+ LEFT JOIN Organisationen ON Spenden.Organisation = Organisationen.ID
+ LEFT JOIN contracts ON Spenden.contract = contracts.id
+ LEFT JOIN Förderer AS ContractPatron ON contracts.patron = ContractPatron.ID
+ LEFT JOIN Organisationen AS ContractOrga ON ContractPatron.Organisation = ContractOrga.ID
+ LEFT JOIN calls ON contracts.`call` = calls.shorthand
+WHERE Spenden.contract = ?
+";
+ $stmt = $mysqli->prepare($sql);
+ $stmt->bind_param("i", $contractID);
+ $stmt->execute();
+ $r = $stmt->get_result();
+ include __DIR__ . '/autotable.php';
+ $stmt->reset();
+})();
+if ($num_rows) {
+ $prohibit = true;
+?>
+ <p class="warning"><i class="fas fa-exclamation-triangle"></i> Es existieren verknüpfte <?=$num_rows?> Spendendatens&auml;tze. Löschen daher nicht möglich.</p>
+<?
+}
+?>
+
+<hr>
+<?
+if (!$prohibit) {
+?>
+ <button class="medium" onclick="return delContract();" style="background: red;"><i class="far fa-trash-alt"></i> Vertragsdatensatz unwiderruflich löschen</button>
+<?
+} else {
+?>
+<p>Löschen derzeit nicht möglich. Zunächst Datensätze umwidmen bzw. Verknüpfungen entfernen.<p>
+<?
+}
+?>
+<script>
+<? minStart(); ?>
+function doSth(what) {
+ $.ajaxSetup({async:false});
+ $.post('/db/main/delcontractdo.php', {
+ 'id': <?=$contractID?>,
+ 'what': what
+ });
+ window.location.reload();
+}
+
+function delContract() {
+ doSth('contract');
+}
+<? minEnd(); ?>
+</script>
+
+<?
+if (!$prohibit && isset($_GET['autodel']) && $_GET['autodel'] == 1) {
+?>
+<script>
+ delContract();
+</script>
+<?
+}
+
+require_once __DIR__ . "/jumper.php";
+exit(0);
diff --git a/delcontractdo.php b/delcontractdo.php
new file mode 100644
index 0000000..f94bd92
--- /dev/null
+++ b/delcontractdo.php
@@ -0,0 +1,41 @@
+<?php
+/*
+ * 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.
+ */
+?>
+<?php
+/* vim: set ts=4 sw=4 et : */
+
+require_once __DIR__ . "/check_auth.php";
+require_once __DIR__ . "/../includes/common.php";
+
+if (!isset($_POST['id']) || !is_numeric($_POST['id']) || intval($_POST['id'] < 1)) {
+ echo "Ungültiger Aufruf (@1).";
+ exit(0);
+}
+if (!isset($_POST['what'])) {
+ echo "Ungültiger Aufruf (@2).";
+ exit(0);
+}
+$what = $_POST['what'];
+
+if ($what === 'contract') {
+ $sql = "DELETE FROM contracts WHERE id=? LIMIT 1;";
+ $stmt = $mysqli->prepare($sql);
+ $stmt->bind_param("i", $_POST['id']);
+ $stmt->execute();
+ echo $mysqli->affected_rows;
+ $stmt->reset();
+} else {
+ echo "Ungültiger Aufruf (@2).";
+}
+
+exit(0);
diff --git a/deldocument.php b/deldocument.php
new file mode 100644
index 0000000..a677367
--- /dev/null
+++ b/deldocument.php
@@ -0,0 +1,46 @@
+<?php
+/*
+ * 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.
+ */
+?>
+<?php
+/* vim: set ts=4 sw=4 et : */
+
+require_once __DIR__ . "/session.php";
+require_once __DIR__ . "/../includes/common.php";
+require_once __DIR__ . "/config.php";
+
+if (isset($_SESSION['ds']))
+ $_ds = true;
+
+include_once __DIR__ . "/header.php";
+
+if (!isset($_SESSION['admin']) || !isset($_GET['uid']) || !isset($_GET['filename']))
+ exit(0);
+
+$uid = $_GET['uid'];
+$fn = $_GET['filename'];
+
+if (strlen($fn) < 5 || strlen($uid) < 1)
+ exit(0);
+
+unlink('/var/www/uploads/' . basename($uid) . '_/' . basename($fn));
+?>
+
+ <p>Das Dokument wurde gelöscht.</p>
+
+ <p>
+ <button onclick="history.back();">Zur&uuml;ck</button>
+ </p>
+
+<?
+include_once __DIR__ . "/footer.php";
+?>
diff --git a/deldonation.php b/deldonation.php
new file mode 100644
index 0000000..b4621f2
--- /dev/null
+++ b/deldonation.php
@@ -0,0 +1,20 @@
+<?php
+/*
+ * 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.
+ */
+?>
+<?php
+/* vim: set ts=4 sw=4 et : */
+
+require_once __DIR__ . "/check_auth.php";
+require_once __DIR__ . "/../includes/common.php";
+
+deleteSimple("DELETE FROM Spenden WHERE ID IN");
diff --git a/delorga.php b/delorga.php
new file mode 100644
index 0000000..4dfd95c
--- /dev/null
+++ b/delorga.php
@@ -0,0 +1,284 @@
+<?php
+/*
+ * 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.
+ */
+?>
+<?
+/* vim: set ts=4 sw=4 et : */
+
+require_once __DIR__ . "/check_auth.php";
+require_once __DIR__ . "/../includes/common.php";
+
+$orgaID = $_GET['id'];
+$prohibit = false;
+
+(function () use ($mysqli, $_title, $orgaID) {
+ $sql = "SELECT * FROM Organisationen WHERE id=?;";
+ $stmt = $mysqli->prepare($sql);
+ $stmt->bind_param('i', $orgaID);
+ $stmt->execute();
+ $r = $stmt->get_result();
+ $l = $r->fetch_object();
+
+ $_title = '<span class="pii">' . $l->Name . '</span> (ID ' . $orgaID . ')';
+ include_once __DIR__ . "/header.php";
+?>
+ <h1 style="display: inline-block;"><span style="color: red; font-weight: bold;">Orgadatensatz löschen:</span> <?=$_title?></h1>
+<?
+ if ($l->ID < 1) {
+?>
+ <p><b>Kein Datensatz mit dieser ID gefunden!</b></p>
+ <p>Wenn Sie gerade gelöscht haben, war das Löschen erfolgreich.</p>
+
+<? if (isset($_GET['autodel']) && $_GET['autodel'] == 1) { ?>
+ <script>
+ window.close();
+ </script>
+<? }
+ exit(0);
+ }
+
+ $def = [
+ "Auszug",
+ [6, '_=3Name', '_PLZ', '_=2Ort']
+ ];
+ include __DIR__ . '/autoform.php';
+ $stmt->reset();
+})();
+
+(function () use ($orgaID) {
+ $globstring = "{" . $orgaID . "orga}_";
+ require_once __DIR__ . "/doc.php";
+})();
+
+(function () use ($mysqli, $orgaID, &$num_rows) {
+ $title = "Spenden";
+ $nochosen = $nofilter = true;
+ $sql = "
+SELECT
+ Spenden.ID AS SpendenID,
+ contract AS VertrID,
+ CONCAT(calls.name, ' [', contracts.call, '] - ', ContractOrga.Name) AS Vertrag,
+ Betrag,
+ DATE(Geldeingang) AS Geldeingang
+FROM Spenden
+ LEFT JOIN contracts ON Spenden.contract = contracts.id
+ LEFT JOIN Förderer AS ContractPatron ON contracts.patron = ContractPatron.ID
+ LEFT JOIN Organisationen AS ContractOrga ON ContractPatron.Organisation = ContractOrga.ID
+ LEFT JOIN calls ON contracts.`call` = calls.shorthand
+WHERE Spenden.Organisation=? OR ContractOrga.ID = ?
+";
+ $stmt = $mysqli->prepare($sql);
+ $stmt->bind_param("ii", $orgaID, $orgaID);
+ $stmt->execute();
+ $r = $stmt->get_result();
+ include __DIR__ . '/autotable.php';
+ $stmt->reset();
+})();
+if ($num_rows) {
+ $prohibit = true;
+?>
+ <p class="warning"><i class="fas fa-exclamation-triangle"></i> Es existieren <?=$num_rows?> Spendendatens&auml;tze, die direkt oder indirekt mit diesem Orgadatensatz verknüpft sind. Löschen daher nicht möglich.</p>
+<?
+}
+
+(function () use ($mysqli, $orgaID, &$num_rows) {
+ $title = "Stipendien";
+ $nochosen = $nofilter = true;
+ $sql = "
+SELECT
+ Stipendien.ID AS StipID,
+ Stipendien.Jahr AS Jahr,
+ Förderarten.Name AS Förderart
+FROM Stipendien
+ LEFT JOIN Förderarten ON Stipendien.Förderart = Förderarten.ID
+ LEFT JOIN Förderer ON Stipendien.Förderer = Förderer.ID
+WHERE Förderer.Organisation=? AND Förderbeginn >= '2000-01-01'
+";
+ $stmt = $mysqli->prepare($sql);
+ $stmt->bind_param("i", $orgaID);
+ $stmt->execute();
+ $r = $stmt->get_result();
+ include __DIR__ . '/autotable.php';
+ $stmt->reset();
+})();
+if ($num_rows) {
+ $prohibit = true;
+?>
+ <p class="warning"><i class="fas fa-exclamation-triangle"></i> Es existieren <?=$num_rows?> Stipendiendatens&auml;tze, die über den Fördererdatensatz mit diesem Orgadatensatz verknüpft sind. Löschen daher nicht möglich.</p>
+<?
+}
+
+(function () use ($mysqli, $orgaID, &$num_rows) {
+ $title = "Notizen";
+ $nochosen = $nofilter = true;
+ $sql = "
+SELECT
+ id AS NotizID,
+ title AS Titel,
+ solved AS erledigt,
+ changets AS `Letzte Änderung`
+FROM notes
+WHERE orgaid=?
+";
+ $stmt = $mysqli->prepare($sql);
+ $stmt->bind_param("i", $orgaID);
+ $stmt->execute();
+ $r = $stmt->get_result();
+ include __DIR__ . '/autotable.php';
+ $stmt->reset();
+})();
+if ($num_rows) {
+ $prohibit = true;
+?>
+ <p class="warning"><i class="fas fa-exclamation-triangle"></i> Es existieren <?=$num_rows?> Notizendatensätze, die mit diesem Orgadatensatz verknüpft sind. Löschen daher nicht möglich.</p>
+<?
+}
+
+(function () use ($mysqli, $orgaID, &$num_rows) {
+ $title = "Organisationen";
+ $nochosen = $nofilter = true;
+ $sql = "
+SELECT
+ ID AS OrgaID,
+ Name AS Organisation
+FROM Organisationen
+WHERE Superorganisation=?
+";
+ $stmt = $mysqli->prepare($sql);
+ $stmt->bind_param("i", $orgaID);
+ $stmt->execute();
+ $r = $stmt->get_result();
+ include __DIR__ . '/autotable.php';
+ $stmt->reset();
+})();
+if ($num_rows) {
+ $prohibit = true;
+?>
+ <p class="warning"><i class="fas fa-exclamation-triangle"></i> Es existieren <?=$num_rows?> Verknüpfungen zu Organisationen, bei der dieser diese Orga die übergeordnete Orga ist. Löschen daher nicht möglich.</p>
+<?
+}
+
+(function () use ($mysqli, $orgaID, &$num_rows) {
+ $title = "Förderer";
+ $nochosen = $nofilter = true;
+ $sql = "
+SELECT
+ Förderer.ID AS FoerdID
+FROM Förderer
+WHERE Förderer.Organisation=?
+";
+ $stmt = $mysqli->prepare($sql);
+ $stmt->bind_param("i", $orgaID);
+ $stmt->execute();
+ $r = $stmt->get_result();
+ include __DIR__ . '/autotable.php';
+ $stmt->reset();
+})();
+if ($num_rows) {
+ $prohibit = true;
+?>
+ <p class="warning"><i class="fas fa-exclamation-triangle"></i> Dieser Orga ist ein Fördererdatensatz zugeordnet. Löschen daher nicht möglich. Bitte zunächst den Fördererdatensatz löschen.</p>
+<?
+}
+
+(function () use ($mysqli, $orgaID, &$num_rows) {
+ $title = "Verträge";
+ $nochosen = $nofilter = true;
+ $sql = "
+SELECT
+ contracts.id AS VertrID,
+ contracts.`call`
+FROM contracts
+ LEFT JOIN Förderer ON contracts.patron = Förderer.ID
+WHERE Förderer.Organisation=?
+";
+ $stmt = $mysqli->prepare($sql);
+ $stmt->bind_param("i", $orgaID);
+ $stmt->execute();
+ $r = $stmt->get_result();
+ include __DIR__ . '/autotable.php';
+ $stmt->reset();
+})();
+if ($num_rows) {
+ $prohibit = true;
+?>
+ <p class="warning"><i class="fas fa-exclamation-triangle"></i> Es existieren Verknüpfungen in <?=$num_rows?> Vertragsdatensätzen. Löschen daher nicht möglich.</p>
+<?
+}
+
+(function () use ($mysqli, $orgaID, &$num_rows) {
+ $title = "Events";
+ $nochosen = $nofilter = true;
+ $sql = "
+SELECT
+ id AS EventID,
+ name AS Name,
+ DATE(date) AS Datum
+FROM events
+WHERE host=?
+";
+ $stmt = $mysqli->prepare($sql);
+ $stmt->bind_param("i", $orgaID);
+ $stmt->execute();
+ $r = $stmt->get_result();
+ include __DIR__ . '/autotable.php';
+ $stmt->reset();
+})();
+if ($num_rows) {
+ $prohibit = true;
+?>
+ <p class="warning"><i class="fas fa-exclamation-triangle"></i> Es existieren <?=$num_rows?> Verknüpfungen zu Events. Löschen daher nicht möglich.</p>
+<?
+}
+?>
+
+<hr>
+<?
+if (!$prohibit) {
+?>
+ <button class="medium" onclick="return delOrga();" style="background: red;"><i class="far fa-trash-alt"></i> Orgadatensatz unwiderruflich löschen</button>
+<?
+} else {
+?>
+<p>Löschen derzeit nicht möglich. Zunächst Datensätze umwidmen bzw. Verknüpfungen entfernen.<p>
+<?
+}
+?>
+
+<script>
+<? minStart(); ?>
+function doSth(what) {
+ $.ajaxSetup({async:false});
+ $.post('/db/main/delorgado.php', {
+ 'id': <?=$orgaID?>,
+ 'what': what
+ });
+ window.location.reload();
+}
+
+function delOrga() {
+ doSth('orga');
+}
+<? minEnd(); ?>
+</script>
+
+<?
+if (!$prohibit && isset($_GET['autodel']) && $_GET['autodel'] == 1) {
+?>
+<script>
+ delOrga();
+</script>
+<?
+}
+
+require_once __DIR__ . "/jumper.php";
+exit(0);
diff --git a/delorgado.php b/delorgado.php
new file mode 100644
index 0000000..b4ad71b
--- /dev/null
+++ b/delorgado.php
@@ -0,0 +1,41 @@
+<?php
+/*
+ * 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.
+ */
+?>
+<?php
+/* vim: set ts=4 sw=4 et : */
+
+require_once __DIR__ . "/check_auth.php";
+require_once __DIR__ . "/../includes/common.php";
+
+if (!isset($_POST['id']) || !is_numeric($_POST['id']) || intval($_POST['id'] < 1)) {
+ echo "Ungültiger Aufruf (@1).";
+ exit(0);
+}
+if (!isset($_POST['what'])) {
+ echo "Ungültiger Aufruf (@2).";
+ exit(0);
+}
+$what = $_POST['what'];
+
+if ($what === 'orga') {
+ $sql = "DELETE FROM Organisationen WHERE ID=? LIMIT 1;";
+ $stmt = $mysqli->prepare($sql);
+ $stmt->bind_param("i", $_POST['id']);
+ $stmt->execute();
+ echo $mysqli->affected_rows;
+ $stmt->reset();
+} else {
+ echo "Ungültiger Aufruf (@2).";
+}
+
+exit(0);
diff --git a/delorgaperson.php b/delorgaperson.php
new file mode 100644
index 0000000..578b5df
--- /dev/null
+++ b/delorgaperson.php
@@ -0,0 +1,20 @@
+<?php
+/*
+ * 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.
+ */
+?>
+<?php
+/* vim: set ts=4 sw=4 et : */
+
+require_once __DIR__ . "/check_auth.php";
+require_once __DIR__ . "/../includes/common.php";
+
+deleteSimple("DELETE FROM Personen_Organisationen WHERE ID IN");
diff --git a/delpatron.php b/delpatron.php
new file mode 100644
index 0000000..b89949b
--- /dev/null
+++ b/delpatron.php
@@ -0,0 +1,179 @@
+<?php
+/*
+ * 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.
+ */
+?>
+<?
+/* vim: set ts=4 sw=4 et : */
+
+require_once __DIR__ . "/check_auth.php";
+require_once __DIR__ . "/../includes/common.php";
+
+$foerdID = $_GET['id'];
+$prohibit = false;
+
+(function () use ($mysqli, $_title, $foerdID) {
+ $sql = "SELECT *, Organisationen.Name, Organisationen.PLZ, Organisationen.Ort FROM Förderer LEFT JOIN Organisationen ON Förderer.Organisation = Organisationen.ID WHERE Förderer.ID=?;";
+ $stmt = $mysqli->prepare($sql);
+ $stmt->bind_param('i', $foerdID);
+ $stmt->execute();
+ $r = $stmt->get_result();
+ $l = $r->fetch_object();
+
+ $_title = '<span class="pii">' . $l->Name . '</span> (ID ' . $foerdID . ')';
+ include_once __DIR__ . "/header.php";
+?>
+ <h1 style="display: inline-block;"><span style="color: red; font-weight: bold;">Fördererdatensatz löschen:</span> <?=$_title?></h1>
+<?
+ if ($l->ID < 1) {
+?>
+ <p><b>Kein Datensatz mit dieser ID gefunden!</b></p>
+ <p>Wenn Sie gerade gelöscht haben, war das Löschen erfolgreich.</p>
+
+<? if (isset($_GET['autodel']) && $_GET['autodel'] == 1) { ?>
+ <script>
+ window.close();
+ </script>
+<? }
+ exit(0);
+ }
+
+ $def = [
+ "Auszug",
+ [6, '_=3Name', '_PLZ', '_=2Ort']
+ ];
+ include __DIR__ . '/autoform.php';
+ $stmt->reset();
+})();
+
+(function () use ($foerdID) {
+ $globstring = "{" . $foerdID . "patron}_";
+ require_once __DIR__ . "/doc.php";
+})();
+
+(function () use ($mysqli, $foerdID, &$num_rows) {
+ $title = "Stipendien";
+ $nochosen = $nofilter = true;
+ $sql = "
+SELECT
+ Stipendien.ID AS StipID,
+ Stipendien.Jahr AS Jahr,
+ Förderarten.Name AS Förderart
+FROM Stipendien
+ LEFT JOIN Förderarten ON Stipendien.Förderart = Förderarten.ID
+WHERE Stipendien.Förderer = ?
+";
+ $stmt = $mysqli->prepare($sql);
+ $stmt->bind_param("i", $foerdID);
+ $stmt->execute();
+ $r = $stmt->get_result();
+ include __DIR__ . '/autotable.php';
+ $stmt->reset();
+})();
+if ($num_rows) {
+ $prohibit = true;
+?>
+ <p class="warning"><i class="fas fa-exclamation-triangle"></i> Es existieren <?=$num_rows?> vernknüpfte Stipendiendatens&auml;tze. Löschen daher nicht möglich.</p>
+<?
+}
+
+(function () use ($mysqli, $foerdID, &$num_rows) {
+ $title = "Notizen";
+ $nochosen = $nofilter = true;
+ $sql = "
+SELECT
+ id AS NotizID,
+ title AS Titel,
+ solved AS erledigt,
+ changets AS `Letzte Änderung`
+FROM notes
+WHERE foerdid=?
+";
+ $stmt = $mysqli->prepare($sql);
+ $stmt->bind_param("i", $foerdID);
+ $stmt->execute();
+ $r = $stmt->get_result();
+ include __DIR__ . '/autotable.php';
+ $stmt->reset();
+})();
+if ($num_rows) {
+ $prohibit = true;
+?>
+ <p class="warning"><i class="fas fa-exclamation-triangle"></i> Es existieren <?=$num_rows?> vernknüpfte Notizendatensätze. Löschen daher nicht möglich.</p>
+<?
+}
+
+(function () use ($mysqli, $foerdID, &$num_rows) {
+ $title = "Verträge";
+ $nochosen = $nofilter = true;
+ $sql = "
+SELECT
+ contracts.id AS VertrID,
+ contracts.`call`
+FROM contracts
+WHERE contracts.patron = ?
+";
+ $stmt = $mysqli->prepare($sql);
+ $stmt->bind_param("i", $foerdID);
+ $stmt->execute();
+ $r = $stmt->get_result();
+ include __DIR__ . '/autotable.php';
+ $stmt->reset();
+})();
+if ($num_rows) {
+ $prohibit = true;
+?>
+ <p class="warning"><i class="fas fa-exclamation-triangle"></i> Es existieren Verknüpfungen in <?=$num_rows?> Vertragsdatensätzen. Löschen daher nicht möglich.</p>
+<?
+}
+?>
+
+<hr>
+<?
+if (!$prohibit) {
+?>
+ <button class="medium" onclick="return delPatron();" style="background: red;"><i class="far fa-trash-alt"></i> Fördererdatensatz unwiderruflich löschen</button>
+<?
+} else {
+?>
+<p>Löschen derzeit nicht möglich. Zunächst Datensätze umwidmen bzw. Verknüpfungen entfernen.<p>
+<?
+}
+?>
+
+<script>
+<? minStart(); ?>
+function doSth(what) {
+ $.ajaxSetup({async:false});
+ $.post('/db/main/delpatrondo.php', {
+ 'id': <?=$foerdID?>,
+ 'what': what
+ });
+ window.location.reload();
+}
+
+function delPatron() {
+ doSth('patron');
+}
+<? minEnd(); ?>
+</script>
+
+<?
+if (!$prohibit && isset($_GET['autodel']) && $_GET['autodel'] == 1) {
+?>
+<script>
+ delPatron();
+</script>
+<?
+}
+
+require_once __DIR__ . "/jumper.php";
+exit(0);
diff --git a/delpatrondo.php b/delpatrondo.php
new file mode 100644
index 0000000..7e29f18
--- /dev/null
+++ b/delpatrondo.php
@@ -0,0 +1,41 @@
+<?php
+/*
+ * 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.
+ */
+?>
+<?php
+/* vim: set ts=4 sw=4 et : */
+
+require_once __DIR__ . "/check_auth.php";
+require_once __DIR__ . "/../includes/common.php";
+
+if (!isset($_POST['id']) || !is_numeric($_POST['id']) || intval($_POST['id'] < 1)) {
+ echo "Ungültiger Aufruf (@1).";
+ exit(0);
+}
+if (!isset($_POST['what'])) {
+ echo "Ungültiger Aufruf (@2).";
+ exit(0);
+}
+$what = $_POST['what'];
+
+if ($what === 'patron') {
+ $sql = "DELETE FROM Förderer WHERE ID=? LIMIT 1;";
+ $stmt = $mysqli->prepare($sql);
+ $stmt->bind_param("i", $_POST['id']);
+ $stmt->execute();
+ echo $mysqli->affected_rows;
+ $stmt->reset();
+} else {
+ echo "Ungültiger Aufruf (@2).";
+}
+
+exit(0);
diff --git a/delperson.php b/delperson.php
new file mode 100644
index 0000000..001ad5e
--- /dev/null
+++ b/delperson.php
@@ -0,0 +1,385 @@
+<?php
+/*
+ * 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.
+ */
+?>
+<?
+/* vim: set ts=4 sw=4 et : */
+
+require_once __DIR__ . "/check_auth.php";
+require_once __DIR__ . "/../includes/common.php";
+
+$persID = $_GET['id'];
+$prohibit = false;
+
+(function () use ($mysqli, $_title, $persID) {
+ $sql = "SELECT *, DATE(Geburtsdatum) AS Geburtsdatum FROM Personen WHERE id=?;";
+ $stmt = $mysqli->prepare($sql);
+ $stmt->bind_param('i', $persID);
+ $stmt->execute();
+ $r = $stmt->get_result();
+ $l = $r->fetch_object();
+
+ $_title = '<span class="pii">' . $l->Nachname . ', ' . $l->Vorname . '</span> (ID ' . $persID . ')';
+ include_once __DIR__ . "/header.php";
+?>
+ <h1 style="display: inline-block;"><span style="color: red; font-weight: bold;">Personendatensatz löschen:</span> <?=$_title?></h1>
+<?
+ if ($l->ID < 1) {
+?>
+ <p><b>Kein Datensatz mit dieser ID gefunden!</b></p>
+ <p>Wenn Sie gerade gelöscht haben, war das Löschen erfolgreich.</p>
+
+<? if (isset($_GET['autodel']) && $_GET['autodel'] == 1) { ?>
+ <script>
+ window.close();
+ </script>
+<? }
+ exit(0);
+ }
+
+ $def = [
+ "Auszug",
+ [6, '_Geschlecht', '_=2Vorname', '_=2Nachname', '_Titel'],
+ [6, '_Geburtsdatum#isodate', '_=2Email', '_PLZ', '_=2Ort']
+ ];
+ include __DIR__ . '/autoform.php';
+ $stmt->reset();
+})();
+?>
+
+<?
+(function () use ($persID) {
+ $globstring = "{" . $persID . "person}_";
+ require_once __DIR__ . "/doc.php";
+})();
+?>
+
+<?
+(function () use ($mysqli, $persID, &$num_rows) {
+ $title = "Spenden";
+ $nochosen = $nofilter = true;
+ $sql = "
+SELECT
+ Spenden.ID AS SpendenID,
+ Spenden.Organisation AS OrgaID,
+ Organisationen.Name AS Organisation,
+ contract AS VertrID,
+ CONCAT(calls.name, ' [', contracts.call, '] - ', ContractOrga.Name) AS Vertrag,
+ Betrag,
+ DATE(Geldeingang) AS Geldeingang
+FROM Spenden
+ LEFT JOIN Organisationen ON Spenden.Organisation = Organisationen.ID
+ LEFT JOIN contracts ON Spenden.contract = contracts.id
+ LEFT JOIN Förderer AS ContractPatron ON contracts.patron = ContractPatron.ID
+ LEFT JOIN Organisationen AS ContractOrga ON ContractPatron.Organisation = ContractOrga.ID
+ LEFT JOIN calls ON contracts.`call` = calls.shorthand
+WHERE Spenden.Person=?
+";
+ $stmt = $mysqli->prepare($sql);
+ $stmt->bind_param("i", $persID);
+ $stmt->execute();
+ $r = $stmt->get_result();
+ include __DIR__ . '/autotable.php';
+ $stmt->reset();
+})();
+if ($num_rows) {
+ $prohibit = true;
+?>
+ <p class="warning"><i class="fas fa-exclamation-triangle"></i> Es existieren <?=$num_rows?> Spendendatens&auml;tze, die mit diesem Personendatensatz verknüpft sind. Löschen daher nicht möglich.</p>
+<?
+}
+?>
+
+<?
+(function () use ($mysqli, $persID, &$num_rows) {
+ $title = "Stipendien";
+ $nochosen = $nofilter = true;
+ $sql = "
+SELECT
+ Stipendien.ID AS StipID,
+ Stipendien.Jahr AS Jahr,
+ Förderarten.Name AS Förderart
+FROM Stipendien
+ LEFT JOIN Förderarten ON Stipendien.Förderart = Förderarten.ID
+WHERE Stipendien.Person=? AND Förderbeginn >= '2000-01-01'
+";
+ $stmt = $mysqli->prepare($sql);
+ $stmt->bind_param("i", $persID);
+ $stmt->execute();
+ $r = $stmt->get_result();
+ include __DIR__ . '/autotable.php';
+ $stmt->reset();
+})();
+if ($num_rows) {
+ $prohibit = true;
+?>
+ <p class="warning"><i class="fas fa-exclamation-triangle"></i> Es existieren <?=$num_rows?> Stipendiendatens&auml;tze, die mit diesem Personendatensatz verknüpft sind. Löschen daher nicht möglich.</p>
+<?
+}
+?>
+
+<?
+(function () use ($mysqli, $persID, &$num_rows) {
+ $title = "Notizen";
+ $nochosen = $nofilter = true;
+ $sql = "
+SELECT
+ id AS NotizID,
+ title AS Titel,
+ solved AS erledigt,
+ changets AS `Letzte Änderung`
+FROM notes
+WHERE persid=?
+";
+ $stmt = $mysqli->prepare($sql);
+ $stmt->bind_param("i", $persID);
+ $stmt->execute();
+ $r = $stmt->get_result();
+ include __DIR__ . '/autotable.php';
+ $stmt->reset();
+})();
+if ($num_rows) {
+ $prohibit = true;
+?>
+ <p class="warning"><i class="fas fa-exclamation-triangle"></i> Es existieren <?=$num_rows?> Notizendatensätze, die mit diesem Personendatensatz verknüpft sind. Löschen daher nicht möglich.</p>
+<?
+}
+?>
+
+<?
+(function () use ($mysqli, $persID, &$num_rows) {
+ $title = "Organisationen";
+ $nochosen = $nofilter = true;
+ $sql = "
+SELECT
+ Personen_Organisationen.Organisation AS OrgaID,
+ Organisationen.Name AS Organisation,
+ Abteilung,
+ Funktion
+FROM Personen_Organisationen
+ LEFT JOIN Organisationen ON Personen_Organisationen.Organisation = Organisationen.ID
+WHERE Personen_Organisationen.Person=?
+";
+ $stmt = $mysqli->prepare($sql);
+ $stmt->bind_param("i", $persID);
+ $stmt->execute();
+ $r = $stmt->get_result();
+ include __DIR__ . '/autotable.php';
+ $stmt->reset();
+})();
+if ($num_rows) {
+ $prohibit = true;
+?>
+ <p class="warning"><i class="fas fa-exclamation-triangle"></i> Es existieren <?=$num_rows?> Verknüpfungen zu Organisationen. Löschen daher nicht möglich.</p>
+ <button class="medium" onclick="return delOrgas();" style="background: red;"><i class="far fa-trash-alt"></i> Alle Organisationsverknüpfungen löschen</button>
+<?
+}
+?>
+
+<?
+(function () use ($mysqli, $persID, &$num_rows) {
+ $title = "Förderer";
+ $nochosen = $nofilter = true;
+ $sql = "
+SELECT
+ Förderer.ID AS FoerdID,
+ Organisationen.Name AS Förderer
+FROM Förderer
+ LEFT JOIN Organisationen ON Förderer.Organisation = Organisationen.ID
+WHERE Förderer.zuwendungen=? OR Förderer.`Ansprechpartner Stipendiaten`=?
+";
+ $stmt = $mysqli->prepare($sql);
+ $stmt->bind_param("ii", $persID, $persID);
+ $stmt->execute();
+ $r = $stmt->get_result();
+ include __DIR__ . '/autotable.php';
+ $stmt->reset();
+})();
+if ($num_rows) {
+ $prohibit = true;
+?>
+ <p class="warning"><i class="fas fa-exclamation-triangle"></i> Es existieren Verknüpfungen in <?=$num_rows?> Fördererdatensätzen. Löschen daher nicht möglich.</p>
+<?
+}
+?>
+
+<?
+(function () use ($mysqli, $persID, &$num_rows) {
+ $title = "Verträge";
+ $nochosen = $nofilter = true;
+ $sql = "
+SELECT
+ contracts.id AS VertrID,
+ contracts.`call`,
+ Organisationen.Name AS Förderer
+FROM contracts
+ LEFT JOIN Förderer ON contracts.patron = Förderer.ID
+ LEFT JOIN Organisationen ON Förderer.Organisation = Organisationen.ID
+WHERE contracts.contact=?
+";
+ $stmt = $mysqli->prepare($sql);
+ $stmt->bind_param("i", $persID);
+ $stmt->execute();
+ $r = $stmt->get_result();
+ include __DIR__ . '/autotable.php';
+ $stmt->reset();
+})();
+if ($num_rows) {
+ $prohibit = true;
+?>
+ <p class="warning"><i class="fas fa-exclamation-triangle"></i> Es existieren Verknüpfungen in <?=$num_rows?> Vertragsdatensätzen. Löschen daher nicht möglich.</p>
+<?
+}
+?>
+
+<?
+(function () use ($mysqli, $persID, &$num_rows) {
+ $title = "Kommissionen";
+ $nochosen = $nofilter = true;
+ $sql = "
+SELECT
+ `call`,
+ Kommissionen.Benutzername AS Shorthand
+FROM commissioners
+LEFT JOIN Kommissionen on commissioners.Kommission = Kommissionen.ID
+WHERE commissioners.Person=?
+";
+ $stmt = $mysqli->prepare($sql);
+ $stmt->bind_param("i", $persID);
+ $stmt->execute();
+ $r = $stmt->get_result();
+ include __DIR__ . '/autotable.php';
+ $stmt->reset();
+})();
+if ($num_rows) {
+ $prohibit = true;
+?>
+ <p class="warning"><i class="fas fa-exclamation-triangle"></i> Es existieren <?=$num_rows?> Verknüpfungen zu Kommissionen. Löschen daher nicht möglich.</p>
+<?
+}
+?>
+
+<?
+(function () use ($mysqli, $persID, &$num_rows) {
+ $title = "Events";
+ $nochosen = $nofilter = true;
+ $sql = "
+SELECT
+ eventid AS EventID,
+ events.name AS Name
+FROM event_participants
+ LEFT JOIN events ON event_participants.eventid = events.id
+WHERE persid=?
+";
+ $stmt = $mysqli->prepare($sql);
+ $stmt->bind_param("i", $persID);
+ $stmt->execute();
+ $r = $stmt->get_result();
+ include __DIR__ . '/autotable.php';
+ $stmt->reset();
+})();
+if ($num_rows) {
+ $prohibit = true;
+?>
+ <p class="warning"><i class="fas fa-exclamation-triangle"></i> Es existieren <?=$num_rows?> Verknüpfungen zu Events. Löschen daher nicht möglich.</p>
+ <button class="medium" onclick="return delEvents();" style="background: red;"><i class="far fa-trash-alt"></i> Alle Eventverknüpfungen löschen</button>
+<?
+}
+?>
+
+<?
+(function () use ($mysqli, $persID, &$num_rows) {
+ $title = "E-Mails";
+ $nochosen = $nofilter = true;
+ $sql = "
+SELECT
+ mails.id AS EmailID,
+ mails.subject AS Betreff,
+ mails.from AS Absender,
+ mails.ts AS Datum
+FROM mails
+WHERE persid=?
+";
+ $stmt = $mysqli->prepare($sql);
+ $stmt->bind_param("i", $persID);
+ $stmt->execute();
+ $r = $stmt->get_result();
+ include __DIR__ . '/autotable.php';
+ $stmt->reset();
+})();
+if ($num_rows) {
+ $prohibit = true;
+?>
+ <p class="warning"><i class="fas fa-exclamation-triangle"></i> Es existieren <?=$num_rows?> verknüpfte E-Mails. Löschen daher nicht möglich.</p>
+ <button class="medium" onclick="return delMails();" style="background: red;"><i class="far fa-trash-alt"></i> Alle verknüpften E-Mails löschen</button>
+ <button class="medium" onclick="return rewMails();" style="background: orange;"><i class="fas fa-eraser"></i> E-Mails beibehalten, alle Verknüpfungen entfernen</button>
+<?
+}
+?>
+
+<hr>
+<?
+if (!$prohibit) {
+?>
+ <button class="medium" onclick="return delPerson();" style="background: red;"><i class="far fa-trash-alt"></i> Personendatensatz unwiderruflich löschen</button>
+<?
+} else {
+?>
+<p>Löschen derzeit nicht möglich. Zunächst Datensätze umwidmen bzw. Verknüpfungen entfernen.<p>
+<?
+}
+?>
+<script>
+<? minStart(); ?>
+function doSth(what) {
+ $.ajaxSetup({async:false});
+ $.post('/db/main/delpersondo.php', {
+ 'id': <?=$persID?>,
+ 'what': what
+ });
+ window.location.reload();
+}
+
+function delOrgas() {
+ doSth('orga');
+}
+
+function delEvents() {
+ doSth('event');
+}
+
+function delMails() {
+ doSth('email');
+}
+
+function rewMails() {
+ doSth('emailrewrite');
+}
+
+function delPerson() {
+ doSth('person');
+}
+<? minEnd(); ?>
+</script>
+
+<?
+if (!$prohibit && isset($_GET['autodel']) && $_GET['autodel'] == 1) {
+?>
+<script>
+ delPerson();
+</script>
+<?
+}
+
+require_once __DIR__ . "/jumper.php";
+exit(0);
+?>
diff --git a/delpersondo.php b/delpersondo.php
new file mode 100644
index 0000000..0af06c7
--- /dev/null
+++ b/delpersondo.php
@@ -0,0 +1,70 @@
+<?php
+/*
+ * 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.
+ */
+?>
+<?php
+/* vim: set ts=4 sw=4 et : */
+
+require_once __DIR__ . "/check_auth.php";
+require_once __DIR__ . "/../includes/common.php";
+
+if (!isset($_POST['id']) || !is_numeric($_POST['id']) || intval($_POST['id'] < 1)) {
+ echo "Ungültiger Aufruf (@1).";
+ exit(0);
+}
+if (!isset($_POST['what'])) {
+ echo "Ungültiger Aufruf (@2).";
+ exit(0);
+}
+$what = $_POST['what'];
+
+if ($what === 'orga') {
+ $sql = "DELETE FROM Personen_Organisationen WHERE Person=? LIMIT 20;";
+ $stmt = $mysqli->prepare($sql);
+ $stmt->bind_param("i", $_POST['id']);
+ $stmt->execute();
+ echo $mysqli->affected_rows;
+ $stmt->reset();
+} else if ($what === 'event') {
+ $sql = "DELETE FROM event_participants WHERE persid=? LIMIT 500;";
+ $stmt = $mysqli->prepare($sql);
+ $stmt->bind_param("i", $_POST['id']);
+ $stmt->execute();
+ echo $mysqli->affected_rows;
+ $stmt->reset();
+} else if ($what === 'email') {
+ $sql = "DELETE FROM mails WHERE persid=? LIMIT 1000;";
+ $stmt = $mysqli->prepare($sql);
+ $stmt->bind_param("i", $_POST['id']);
+ $stmt->execute();
+ echo $mysqli->affected_rows;
+ $stmt->reset();
+} else if ($what === 'emailrewrite') {
+ $sql = "UPDATE mails SET persid=NULL WHERE persid=? LIMIT 1000;";
+ $stmt = $mysqli->prepare($sql);
+ $stmt->bind_param("i", $_POST['id']);
+ $stmt->execute();
+ echo $mysqli->affected_rows;
+ $stmt->reset();
+} else if ($what === 'person') {
+ $sql = "DELETE FROM Personen WHERE ID=? LIMIT 1;";
+ $stmt = $mysqli->prepare($sql);
+ $stmt->bind_param("i", $_POST['id']);
+ $stmt->execute();
+ echo $mysqli->affected_rows;
+ $stmt->reset();
+} else {
+ echo "Ungültiger Aufruf (@2).";
+}
+
+exit(0);
+?>
diff --git a/delstip.php b/delstip.php
new file mode 100644
index 0000000..fd644a8
--- /dev/null
+++ b/delstip.php
@@ -0,0 +1,133 @@
+<?php
+/*
+ * 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.
+ */
+?>
+<?
+/* vim: set ts=4 sw=4 et : */
+
+require_once __DIR__ . "/check_auth.php";
+require_once __DIR__ . "/../includes/common.php";
+
+$stipID = $_GET['id'];
+$prohibit = false;
+
+(function () use ($mysqli, $_title, $stipID) {
+ $sql = "SELECT Personen.*, Stipendien.Jahr FROM Stipendien LEFT JOIN Personen ON Stipendien.Person = Personen.ID WHERE Stipendien.ID=?;";
+ $stmt = $mysqli->prepare($sql);
+ $stmt->bind_param('i', $stipID);
+ $stmt->execute();
+ $r = $stmt->get_result();
+ $l = $r->fetch_object();
+
+ $_title = '<span class="pii">' . $l->Nachname . ', ' . $l->Vorname . ' (' . $l->Jahr . ')</span> (ID ' . $stipID . ')';
+ include_once __DIR__ . "/header.php";
+?>
+ <h1 style="display: inline-block;"><span style="color: red; font-weight: bold;">Stipendiendatensatz löschen:</span> <?=$_title?></h1>
+<?
+ if ($l->ID < 1) {
+?>
+ <p><b>Kein Datensatz mit dieser ID gefunden!</b></p>
+ <p>Wenn Sie gerade gelöscht haben, war das Löschen erfolgreich.</p>
+
+<? if (isset($_GET['autodel']) && $_GET['autodel'] == 1) { ?>
+ <script>
+ window.close();
+ </script>
+<? }
+ exit(0);
+ }
+
+ $def = [
+ "Auszug",
+ [6, '_=2Vorname', '_=2Nachname', '_Jahr'],
+ ];
+ include __DIR__ . '/autoform.php';
+ $stmt->reset();
+})();
+
+(function () use ($stipID) {
+ $globstring = "{" . $stipID . "stip}_";
+ require_once __DIR__ . "/doc.php";
+})();
+
+(function () use ($stipID) {
+ $globstring = $stipID . "_";
+ require_once __DIR__ . "/doc.php";
+})();
+
+(function () use ($mysqli, $stipID, &$num_rows) {
+ $title = "Notizen";
+ $nochosen = $nofilter = true;
+ $sql = "
+SELECT
+ id AS NotizID,
+ title AS Titel,
+ solved AS erledigt,
+ changets AS `Letzte Änderung`
+FROM notes
+WHERE stipid=?
+";
+ $stmt = $mysqli->prepare($sql);
+ $stmt->bind_param("i", $stipID);
+ $stmt->execute();
+ $r = $stmt->get_result();
+ include __DIR__ . '/autotable.php';
+ $stmt->reset();
+})();
+if ($num_rows) {
+ $prohibit = true;
+?>
+ <p class="warning"><i class="fas fa-exclamation-triangle"></i> Es existieren <?=$num_rows?> Notizendatensätze, die mit diesem Personendatensatz verknüpft sind. Löschen daher nicht möglich.</p>
+<?
+}
+?>
+
+<hr>
+<?
+if (!$prohibit) {
+?>
+ <button class="medium" onclick="return delStip();" style="background: red;"><i class="far fa-trash-alt"></i> Stipendiendatensatz unwiderruflich löschen</button>
+<?
+} else {
+?>
+<p>Löschen derzeit nicht möglich. Zunächst Datensätze umwidmen bzw. Verknüpfungen entfernen.<p>
+<?
+}
+?>
+<script>
+<? minStart(); ?>
+function doSth(what) {
+ $.ajaxSetup({async:false});
+ $.post('/db/main/delstipdo.php', {
+ 'id': <?=$stipID?>,
+ 'what': what
+ });
+ window.location.reload();
+}
+
+function delStip() {
+ doSth('stip');
+}
+<? minEnd(); ?>
+</script>
+
+<?
+if (!$prohibit && isset($_GET['autodel']) && $_GET['autodel'] == 1) {
+?>
+<script>
+ delStip();
+</script>
+<?
+}
+
+require_once __DIR__ . "/jumper.php";
+exit(0);
diff --git a/delstipdo.php b/delstipdo.php
new file mode 100644
index 0000000..c1695ba
--- /dev/null
+++ b/delstipdo.php
@@ -0,0 +1,41 @@
+<?php
+/*
+ * 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.
+ */
+?>
+<?php
+/* vim: set ts=4 sw=4 et : */
+
+require_once __DIR__ . "/check_auth.php";
+require_once __DIR__ . "/../includes/common.php";
+
+if (!isset($_POST['id']) || !is_numeric($_POST['id']) || intval($_POST['id'] < 1)) {
+ echo "Ungültiger Aufruf (@1).";
+ exit(0);
+}
+if (!isset($_POST['what'])) {
+ echo "Ungültiger Aufruf (@2).";
+ exit(0);
+}
+$what = $_POST['what'];
+
+if ($what === 'stip') {
+ $sql = "DELETE FROM Stipendien WHERE ID=? LIMIT 1;";
+ $stmt = $mysqli->prepare($sql);
+ $stmt->bind_param("i", $_POST['id']);
+ $stmt->execute();
+ echo $mysqli->affected_rows;
+ $stmt->reset();
+} else {
+ echo "Ungültiger Aufruf (@2).";
+}
+
+exit(0);
diff --git a/deltemplate.php b/deltemplate.php
new file mode 100644
index 0000000..bfe2126
--- /dev/null
+++ b/deltemplate.php
@@ -0,0 +1,37 @@
+<?php
+/*
+ * 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.
+ */
+?>
+<?php
+/* vim: set ts=4 sw=4 et : */
+
+require_once __DIR__ . "/check_auth.php";
+require_once __DIR__ . "/../includes/common.php";
+
+if (!isset($_POST['id']))
+ exit(0);
+
+$sql = "";
+if (isset($_POST['email']))
+ $sql = "DELETE FROM email_templates WHERE id=? LIMIT 1;";
+else if (isset($_POST['pdf']))
+ $sql = "DELETE FROM pdf_templates WHERE id=? LIMIT 1;";
+
+$stmt = $mysqli->prepare($sql);
+$stmt->bind_param('i', $_POST['id']);
+$stmt->execute();
+$stmt->reset();
+$mysqli->close();
+
+echo "1";
+
+exit(0);
diff --git a/donation.php b/donation.php
new file mode 100644
index 0000000..dd5a29e
--- /dev/null
+++ b/donation.php
@@ -0,0 +1,206 @@
+<?php
+/*
+ * 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.
+ */
+?>
+<?php
+/* vim: set ts=4 sw=4 et : */
+
+require_once __DIR__ . "/check_auth.php";
+require_once __DIR__ . "/../includes/common.php";
+require_once __DIR__ . "/lookup.php";
+require_once __DIR__ . "/lookup_more.php";
+
+(function () use ($mysqli) {
+ global $donationID, $persID, $orgaID;
+
+ $donationID = $_GET['id'];
+ assureInt($donationID) || dead();
+
+ $sql = "
+SELECT
+ Spenden.*,
+ Spenden.contract AS VertrID,
+
+ Spenden.Organisation AS OrgaID,
+
+ DATE(Spenden.Geldeingang) AS Geldeingang,
+ Spendenarten.Name AS Spendenart,
+
+ Spenden.Person AS PersID,
+ CONCAT(Personen.Nachname, ', ', Personen.Vorname) AS Name,
+
+ Spenden.`Spendenquittung Person` AS QuittPersID,
+ CONCAT(APerson.Nachname, ', ', APerson.Vorname) AS `Spendenquittung Person`,
+
+ IF(Förderer.zuwendungen > 0, FPerson.Anrede, Personen.Anrede) AS Anrede,
+ IF(Förderer.zuwendungen > 0, FPerson.Ansprache, Personen.Ansprache) AS Ansprache,
+ IF(Förderer.zuwendungen > 0, FPerson.Vorname, Personen.Vorname) AS Vorname,
+ IF(Förderer.zuwendungen > 0, FPerson.Nachname, Personen.Nachname) AS Nachname,
+ IF(Förderer.zuwendungen > 0, FPerson.Email, Personen.Email) AS Email,
+ Förderer.zahlungsanmerkung AS Zahlungsanmerkung,
+
+ SuperSpende.Betrag AS SupBetrag,
+ DATE(SuperSpende.Geldeingang) AS SupGeldeingang,
+
+ SuperSpende.Person AS SupPerson,
+ CONCAT(SuperSpendePerson.Nachname, ', ', SuperSpendePerson.Vorname) AS SupPersonName,
+
+ SuperSpende.Organisation AS SupOrga,
+ SuperSpendeOrga.Name AS SupOrgaName,
+
+ (SuperSpende.Betrag - (SELECT SUM(SumSpende.Betrag) AS BetrUsed FROM Spenden AS SumSpende WHERE SumSpende.SuperID = Spenden.SuperID)) AS SupUnused
+
+FROM Spenden
+
+LEFT JOIN Personen ON Spenden.Person = Personen.ID
+LEFT JOIN Organisationen ON Spenden.Organisation = Organisationen.ID
+LEFT JOIN Förderer ON Förderer.Organisation = Organisationen.ID
+LEFT JOIN Spendenarten ON Spenden.Förderart = Spendenarten.ID
+LEFT JOIN Personen AS APerson ON Spenden.`Spendenquittung Person` = APerson.ID
+LEFT JOIN Personen AS FPerson ON Förderer.zuwendungen = FPerson.ID
+
+LEFT JOIN Spenden AS SuperSpende ON Spenden.SuperID = SuperSpende.ID
+LEFT JOIN Personen AS SuperSpendePerson ON SuperSpende.Person = SuperSpendePerson.ID
+LEFT JOIN Organisationen AS SuperSpendeOrga ON SuperSpende.Organisation = SuperSpendeOrga.ID
+";
+
+ $email_sql = $sql . " WHERE Spenden.ID IN ";
+ $sql .= "WHERE Spenden.ID=?";
+ $stmt = $mysqli->prepare($sql);
+ $stmt->bind_param('i', $donationID);
+ $stmt->execute();
+ $r = $stmt->get_result();
+ $l = $r->fetch_object();
+ $persID = $l->PersID;
+ $orgaID = $l->OrgaID;
+
+ $_title = 'Spende (ID ' . $donationID . ')';
+ include_once __DIR__ . "/header.php";
+
+ $pdf_sql = "
+SELECT
+ Spenden.ID AS SpendenID,
+ Person AS PersID,
+ CONCAT (
+ IF(Spenden.Organisation > 0, CONCAT(Organisationen.ID, 'orga', '-'), ''),
+ IF(Förderer.zuwendungen > 0, CONCAT(FPerson.ID, 'person', '-'), ''),
+ IF(Spenden.Person > 0, CONCAT(Spenden.Person, 'person', '-'), ''),
+ Spenden.ID, 'donation'
+ ) AS uid,
+ CONCAT(Personen.Nachname, ', ', Personen.Vorname) AS Name,
+ Spenden.Organisation AS OrgaID,
+ Organisationen.Name AS Organisation,
+ Kommentar,
+ Betrag,
+ DATE(Geldeingang) AS Geldeingang,
+ Förderjahr,
+ Spendenarten.Name AS Spendenart,
+ `Spendenquittung Person` AS QuittPersID,
+ CONCAT(APerson.Nachname, ', ', APerson.Vorname) AS `Spendenquittung Person`,
+ IF(Förderer.zuwendungen > 0, FPerson.Anrede, Personen.Anrede) AS Anrede,
+ IF(Förderer.zuwendungen > 0, FPerson.Ansprache, Personen.Ansprache) AS Ansprache,
+ IF(Förderer.zuwendungen > 0, FPerson.Vorname, Personen.Vorname) AS Vorname,
+ IF(Förderer.zuwendungen > 0, FPerson.Nachname, Personen.Nachname) AS Nachname,
+ IF(Förderer.zuwendungen > 0, FPerson.Email, Personen.Email) AS Email,
+ IF(Spenden.Organisation > 0, Organisationen.strasse, Personen.Straße) AS Strasse,
+ IF(Spenden.Organisation > 0, Organisationen.plz, Personen.PLZ) AS PLZ,
+ IF(Spenden.Organisation > 0, Organisationen.ort, Personen.Ort) AS Ort,
+ Förderer.zahlungsanmerkung AS Zahlungsanmerkung
+
+FROM Spenden
+
+LEFT JOIN Personen ON Spenden.Person = Personen.ID
+LEFT JOIN Organisationen ON Spenden.Organisation = Organisationen.ID
+LEFT JOIN Förderer ON Förderer.Organisation = Organisationen.ID
+LEFT JOIN Spendenarten ON Spenden.Förderart = Spendenarten.ID
+LEFT JOIN Personen AS APerson ON Spenden.`Spendenquittung Person` = APerson.ID
+LEFT JOIN Personen AS FPerson ON Förderer.zuwendungen = FPerson.ID
+";
+ $pdf_sql .= " WHERE Spenden.ID IN ";
+
+ $id = "donview";
+ include __DIR__ . '/autoemail.php';
+ include __DIR__ . '/autopdf.php';
+?>
+
+<h1 style="display: inline-block;"><?=$_title?></h1>
+
+&nbsp;<button class="small" onclick="return doEmail_donview(<?=$donationID?>);"><i class="fas fa-envelope"></i> E-Mail senden</button>
+<? if (!$l->SuperID) { ?>
+&nbsp;<button class="small" onclick="return doPDF_donview(<?=$donationID?>);"><i class="fas fa-file-pdf"></i> PDF generieren</button>
+&nbsp;<button class="small" onclick="window.location = '/db/main/newsubdonation.php?superid=<?=$donationID?>';"><i class="fas fa-coins"></i> Spendenaufteilung anlegen</button>
+<? } ?>
+&nbsp;<button class="small" style="background-color: red;" onclick="location.href='/db/main/deldonation.php?id=<?=$donationID?>';"><i class="fas fa-trash-alt"></i></sup> Datensatz löschen</button>
+
+<?php
+ global $persXorga, $contracts, $contractsall;
+
+ _lookup_contracts($orgaID);
+ $contractsorga = $contracts;
+ _lookup_contracts('');
+ $contractsall = array_replace($contractsorga, $contracts);
+
+ $sql = "SELECT Personen.ID, Personen.Nachname, Personen.Vorname FROM Personen_Organisationen INNER JOIN Personen ON Personen_Organisationen.Person = Personen.ID WHERE Personen_Organisationen.Organisation = ? ORDER BY Personen.Nachname";
+ $stmt = $mysqli->prepare($sql);
+ $stmt->bind_param('i', $orgaID);
+ $stmt->bind_result($id, $nachname, $vorname);
+ $stmt->execute();
+ $persXorga = [];
+ while ($stmt->fetch()) {
+ $persXorga[$id] = $nachname . ", " . $vorname;
+ }
+ $stmt->reset();
+
+ if ($l->SuperID) {
+ $id = "subdonation";
+ $entrytable = "Spenden";
+ $def = [
+ "Aufteilung der zugrundeliegenden Super-Spende",
+ [6, 'Betrag', '_VertrID', '=4contract|contractsall~an Vertrag'],
+ [1, 'Kommentar'],
+ "Zugrundliegende Super-Spende",
+ [5, '_SuperID~SuperSpendenID', '_SupBetrag~Betrag', '_SupGeldeingang#isodate~Geldeingang', '_=2SupUnused~nicht aufgeteilter Betrag'],
+ "Spender",
+ [10, '_SupPerson~PersID', '_=9SupPersonName~Name'],
+ [10, '_SupOrga~OrgaID', '_=9SupOrgaName~Organisation'],
+ ];
+ } else {
+ $id = "donation";
+ $entrytable = "Spenden";
+ $def = [
+ "Allgemein",
+ [5, 'Betrag', 'Geldeingang#isodate', 'Förderjahr', '_Spendenart'],
+ [1, 'Kommentar'],
+ [5, '_VertrID', '=4contract|contractsall~Vertrag'],
+ "Spender",
+ [10, '_PersID', '=2Person#search_pers_id~Person setzen', '_=5Name'],
+ [10, '_OrgaID', '=5Organisation|organisationen', '_=4Zahlungsanmerkung'],
+ ];
+ $links = [
+ 'QuittPersID' => '/db/person',
+ ];
+ }
+
+ include __DIR__ . '/autoform.php';
+ $stmt->reset();
+})();
+
+(function () use ($donationID) {
+ $globstring = "{*-" . $donationID . "donation*," . $donationID . "donation*}_";
+ require_once __DIR__ . "/doc.php";
+ })();
+
+include_once __DIR__ . '/donationsXdonation.php';
+
+include_once __DIR__ . '/persXorga.php';
+
+include_once __DIR__ . '/footer.php';
diff --git a/donations.php b/donations.php
new file mode 100644
index 0000000..428616c
--- /dev/null
+++ b/donations.php
@@ -0,0 +1,197 @@
+<?php
+/*
+ * 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.
+ */
+?>
+<?
+/* vim: set ts=4 sw=4 et : */
+
+$_title = "Alle Spenden";
+
+require_once __DIR__ . "/check_auth.php";
+
+$_constraint = "";
+if (isset($_GET['year']) && $_GET['year'] > 2000) {
+ $_constraint = " WHERE Geldeingang >= '" . intval($_GET['year']) . "-01-01' AND Geldeingang < '" . (intval($_GET['year']) + 1) . "-01-01'";
+ $_title .= " (Jahr " . $_GET['year'] . ")";
+}
+if (isset($_GET['ay']) && $_GET['ay'] > 2000) {
+ $_constraint = " WHERE Geldeingang >= '" . intval($_GET['ay']) . "-09-01' AND Geldeingang < '" . (intval($_GET['ay']) + 1) . "-09-01'";
+ $_title .= " (Förderjahr/Akad. Jahr an FH " . $_GET['ay'] . "/" . (intval($_GET['ay'])+1) . ")";
+}
+if (isset($_GET['call']) && $_GET['call'] != "") {
+ $_constraint = " WHERE `call` = '" . $_GET['call'] . "'";
+ $_title .= " (Call " . $_GET['call'] . ")";
+}
+if (isset($_GET['last']) && $_GET['last'] != "") {
+ $_constraint = " ORDER BY Spenden.ID DESC LIMIT " . intval($_GET['last']);
+ $_title .= " (Letzte " . $_GET['last'] . " <small>nach ID</small>)";
+}
+
+include_once __DIR__ . "/header.php";
+require_once __DIR__ . "/../includes/common.php";
+require_once __DIR__ . "/../includes/db2.php";
+
+doTitle();
+
+(function () use ($_constraint, $mysqli2) {
+ $id = "donations5";
+ $sql = "
+SELECT
+ Spenden.ID AS SpendenID,
+ Spenden.SuperID AS SuperSpendenID,
+ Person AS PersID,
+ CONCAT(Personen.Nachname, ', ', Personen.Vorname) AS Name,
+ Spenden.Organisation AS OrgaID,
+ Organisationen.Name AS Organisation,
+ legal.name AS Rechtsform,
+ contract AS VertrID,
+ CONCAT(calls.name, ' [', contracts.call, '] - ', ContractOrga.Name) AS Vertrag,
+ Kommentar,
+ Betrag,
+ DATE(Geldeingang) AS Geldeingang,
+ Förderjahr,
+ Spendenarten.Name AS Spendenart,
+ FPerson.Anrede, FPerson.Ansprache, FPerson.Vorname, FPerson.Nachname, FPerson.Geschlecht,
+ Organisationen.strasse AS Strasse,
+ Organisationen.plz AS PLZ,
+ Organisationen.ort AS Ort,
+ Förderer.zahlungsanmerkung AS Zahlungsanmerkung
+
+FROM Spenden
+
+LEFT JOIN Personen ON Spenden.Person = Personen.ID
+LEFT JOIN Organisationen ON Spenden.Organisation = Organisationen.ID
+LEFT JOIN Förderer ON Förderer.Organisation = Organisationen.ID
+LEFT JOIN Spendenarten ON Spenden.Förderart = Spendenarten.ID
+LEFT JOIN Personen AS FPerson ON Förderer.zuwendungen = FPerson.ID
+LEFT JOIN contracts ON Spenden.contract = contracts.id
+LEFT JOIN Förderer AS ContractPatron ON contracts.patron = ContractPatron.ID
+LEFT JOIN Organisationen AS ContractOrga ON ContractPatron.Organisation = ContractOrga.ID
+LEFT JOIN calls ON contracts.`call` = calls.shorthand
+LEFT JOIN legal ON Förderer.legal = legal.id
+";
+ $sql .= $_constraint;
+ $getthdef = true;
+ $order = '[[1, "desc"]]';
+ $entrytable = 'Spenden';
+ $idcell = 'SpendenID';
+ $idcellreal = 'ID';
+ $types = [
+ 'Spendenquittung erstellt' => 'checkbox'
+ ];
+ $editable = [
+ 'Kommentar',
+ 'Betrag',
+ 'Geldeingang',
+ 'Förderjahr',
+ 'Spendenquittung erstellt',
+ 'Spendenquittung verschickt'
+ ];
+ $callback_heading = "Dokumente u. Mails";
+
+ $globs = glob(__DIR__ . "/../uploads/*donation*_/*.pdf", GLOB_BRACE|GLOB_NOSORT|GLOB_ERR);
+ $callback = function ($x) use ($globs, $_constraint, $mysqli2) {
+
+ foreach($globs as $g) {
+ //foreach (glob(__DIR__ . "/../uploads/{" . $x . "donation*,*-" . $x . "donation*" . "}_/*.pdf", GLOB_BRACE|GLOB_NOSORT|GLOB_ERR) as $d) {
+ if (strstr($g, "/" . $x . "donation") !== false || strstr($g, "-" . $x . "donation") !== false) {
+ echo '<a style="color: blue !important;" href="/db/main/download.php?document&filename=' . basename($g) . '&uid=' . substr(basename(dirname($g)), 0, -1) . '">' . basename($g) . '</a>';
+ echo '&nbsp;<a onclick="return confirm(\'Wirklich unwiderruflich entsorgen?\');" style="color: blue !important;" href="/db/main/deldocument.php?filename=' . basename($g). '&uid=' . substr(basename(dirname($g)), 0, -1) . '"><i style="color: red !important;" class="fas fa-trash-alt"></i></a>';
+ echo '<br />';
+ }
+ }
+
+ $sql2 = "select ID FROM mails WHERE MATCH (attached) AGAINST ('" . $x . "donation*' IN BOOLEAN MODE)";
+ $stmt2 = $mysqli2->prepare($sql2);
+ $stmt2->bind_result($s);
+ $stmt2->execute();
+ while ($stmt2->fetch()) {
+ echo "<a href='/db/email/" . $s . "'>[Mail " . $s . "]</a><br />";
+ }
+ $stmt2->reset();
+ };
+ $checkboxes = true;
+
+ $bottom = <<<EOD
+<div class="admin_actionsbelow">
+ Ausgewählte
+ <button class="medium" style="background: darkorange;" onclick="return delDonation();"><i class="fas fa-trash-alt"></i> entfernen</button>
+</div>
+EOD;
+
+ include __DIR__ . '/autotable.php';
+
+ $pdf_sql = "
+SELECT
+ Spenden.ID AS SpendenID,
+ Person AS PersID,
+ CONCAT (
+ IF(Spenden.Organisation > 0, CONCAT(Organisationen.ID, 'orga', '-'), ''),
+ IF(Förderer.zuwendungen > 0, CONCAT(FPerson.ID, 'person', '-'), ''),
+ IF(Spenden.Person > 0, CONCAT(Spenden.Person, 'person', '-'), ''),
+ Spenden.ID, 'donation'
+ ) AS uid,
+ CONCAT(Personen.Nachname, ', ', Personen.Vorname) AS Name,
+ Spenden.Organisation AS OrgaID,
+ Organisationen.Name AS Organisation,
+ Kommentar,
+ Betrag,
+ DATE(Geldeingang) AS Geldeingang,
+ Förderjahr,
+ Spendenarten.Name AS Spendenart,
+ `Spendenquittung erstellt`,
+ DATE(`Spendenquittung verschickt`) AS `Spendenquittung verschickt`,
+ `Spendenquittung Person` AS QuittPersID,
+ IF(Förderer.zuwendungen > 0, FPerson.Anrede, Personen.Anrede) AS Anrede,
+ IF(Förderer.zuwendungen > 0, FPerson.Ansprache, Personen.Ansprache) AS Ansprache,
+ IF(Förderer.zuwendungen > 0, FPerson.Vorname, Personen.Vorname) AS Vorname,
+ IF(Förderer.zuwendungen > 0, FPerson.Nachname, Personen.Nachname) AS Nachname,
+ IF(Förderer.zuwendungen > 0, FPerson.Geschlecht, Personen.Geschlecht) AS Geschlecht,
+ IF(Förderer.zuwendungen > 0, FPerson.Email, Personen.Email) AS Email,
+ IF(Spenden.Organisation > 0, Organisationen.strasse, Personen.Straße) AS Strasse,
+ IF(Spenden.Organisation > 0, Organisationen.plz, Personen.PLZ) AS PLZ,
+ IF(Spenden.Organisation > 0, Organisationen.ort, Personen.Ort) AS Ort
+
+FROM Spenden
+
+LEFT JOIN Personen ON Spenden.Person = Personen.ID
+LEFT JOIN Organisationen ON Spenden.Organisation = Organisationen.ID
+LEFT JOIN Förderer ON Förderer.Organisation = Organisationen.ID
+LEFT JOIN Spendenarten ON Spenden.Förderart = Spendenarten.ID
+LEFT JOIN Personen AS FPerson ON Förderer.zuwendungen = FPerson.ID
+";
+ $pdf_sql .= " WHERE Spenden.ID IN ";
+ $email_sql = $pdf_sql;
+ $pdf_file = "spendenbescheinigung";
+ include __DIR__ . '/autoactions.php';
+ include __DIR__ . '/autoemail.php';
+ include __DIR__ . '/autopdf.php';
+?>
+
+<script>
+function delDonation() {
+ $.ajaxSetup({async:false});
+ $.ajax({
+ type: 'POST',
+ url: '/db/main/deldonation.php',
+ data: {
+ 'ids': getIDs_<?=$id?>()
+ }
+ });
+ window.location.reload();
+ return false;
+}
+</script>
+<?
+})();
+
+include_once __DIR__ . "/footer.php";
diff --git a/donationsXdonation.php b/donationsXdonation.php
new file mode 100644
index 0000000..c8bfba9
--- /dev/null
+++ b/donationsXdonation.php
@@ -0,0 +1,40 @@
+<?php
+/*
+ * 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.
+ */
+?>
+<?php
+/* vim: set ts=4 sw=4 et : */
+
+(function () use ($donationID) {
+ if (!assureInt($donationID)) return;
+
+ $id = "donationsXdonation";
+ $title = "Aufteilung zur Spende";
+ $sql = "
+SELECT
+ Spenden.ID AS SpendenID,
+ Spenden.Betrag,
+ Spenden.contract AS VertrID,
+ CONCAT(contracts.`call`, ' - ', Organisationen.Name) AS Vertrag
+
+FROM Spenden
+
+LEFT JOIN contracts ON Spenden.contract = contracts.id
+LEFT JOIN Förderer ON contracts.patron = Förderer.ID
+LEFT JOIN Organisationen ON Förderer.Organisation = Organisationen.ID
+
+WHERE Spenden.SuperID = " . $donationID;
+ $thdef = ['SpendenID', 'Betrag', 'VertrID', 'Vertrag'];
+ $order = '[[2, "desc"]]';
+ $ajax = $nospinner = true;
+ include __DIR__ . '/autotable.php';
+})();
diff --git a/donationsXorga.php b/donationsXorga.php
new file mode 100644
index 0000000..c23063a
--- /dev/null
+++ b/donationsXorga.php
@@ -0,0 +1,43 @@
+<?php
+/*
+ * 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.
+ */
+?>
+<?php
+/* vim: set ts=4 sw=4 et : */
+
+(function () use ($orgaID) {
+ if (!assureInt($orgaID)) return;
+
+ $id = "donationsXorga2";
+ $title = "Spenden von Organisation";
+ $sql = "
+SELECT
+ Spenden.ID AS SpendenID,
+ Spenden.Betrag,
+ DATE(Spenden.Geldeingang) AS Geldeingang,
+ Spenden.contract AS VertrID,
+ IF(Spenden.contract > 0, CONCAT(contracts.`call`, '-', Organisationen.Name), '') AS Vertrag,
+ Spenden.Person AS PersID,
+ Personen.Nachname,
+ Personen.Vorname
+FROM Spenden
+LEFT JOIN Personen ON Spenden.Person = Personen.ID
+LEFT JOIN contracts ON Spenden.contract = contracts.ID
+LEFT JOIN Förderer ON contracts.patron = Förderer.ID
+LEFT JOIN Organisationen ON Förderer.Organisation = Organisationen.ID
+WHERE Spenden.Organisation=" . $orgaID;
+
+ $order = '[[1, "desc"]]';
+ $thdef = ['SpendenID', 'Betrag', 'Geldeingang', 'VertrID', 'Vertrag', 'PersID', 'Nachname', 'Vorname'];
+ $ajax = $nospinner = true;
+ include __DIR__ . '/autotable.php';
+})();
diff --git a/donationsXpers.php b/donationsXpers.php
new file mode 100644
index 0000000..102028c
--- /dev/null
+++ b/donationsXpers.php
@@ -0,0 +1,39 @@
+<?php
+/*
+ * 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.
+ */
+?>
+<?
+/* vim: set ts=4 sw=4 et : */
+
+(function () use ($persID) {
+ if (!assureInt($persID)) return;
+
+ $title = "Spenden zur Person";
+ $thdef = ["SpendenID", "Betrag", "Geldeingang", "VertrID", "Vertrag", "Organisation"];
+ $order = '[[3, "desc"], [1, "desc"]]';
+ $sql = "
+SELECT
+ Spenden.ID AS SpendenID,
+ Spenden.Betrag,
+ DATE(Spenden.Geldeingang) AS Geldeingang,
+ Spenden.contract AS VertrID,
+ IF(Spenden.contract > 0, CONCAT(contracts.`call`, '-', Organisationen.Name), '') AS Vertrag,
+ Organisationen.Name AS Organisation
+FROM Spenden
+LEFT JOIN contracts ON Spenden.contract = contracts.ID
+LEFT JOIN Förderer ON contracts.patron = Förderer.ID
+LEFT JOIN Organisationen ON Förderer.Organisation = Organisationen.ID
+WHERE Spenden.Person=" . $persID . "
+";
+ $ajax = $nospinner = true;
+ include __DIR__ . '/autotable.php';
+})();
diff --git a/dopdf.php b/dopdf.php
new file mode 100644
index 0000000..ca254dc
--- /dev/null
+++ b/dopdf.php
@@ -0,0 +1,202 @@
+<?php
+/*
+ * 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.
+ */
+?>
+<?php
+/* vim: set ts=4 sw=4 et : */
+
+require_once __DIR__ . "/check_auth.php";
+require_once __DIR__ . "/../includes/common.php";
+
+require_once __DIR__ . '/vendor/autoload.php';
+
+require_once __DIR__ . "/twig.php";
+require_once __DIR__ . "/lookup.php";
+
+session_write_close();
+
+$puid = "invalid";
+if (isset($_SESSION['uid'])) {
+ $puid = $_SESSION['uid'];
+}
+
+if (isset($_GET['id']) && ($_SESSION['admin'] || $_SESSION['commissioner'])) {
+ $puid = $_GET['id'];
+}
+
+if (isset($_POST['uid']) && ($_SESSION['admin'] || $_SESSION['commissioner'])) {
+ $puid = $_POST['uid'];
+}
+
+if (isset($_POST['ruid']) && $_SESSION['admin']) {
+ $puid = $_POST['ruid'];
+}
+
+if (isset($_SESSION['commissioner']) && !isset($_SESSION['admin'])) {
+ if (!uidInCommission($puid, $_SESSION['commissioner'])) {
+ header("HTTP/1.0 404 Not Found");
+ exit(0);
+ }
+}
+
+$force = false;
+if (isset($doFull) && $doFull) {
+ if (isset($_GET['force']) && $_GET['force'])
+ $force = true;
+} else {
+ $doFull = false;
+}
+
+if ($puid == 'invalid' && $puid == '') {
+ $mysqli->close();
+ exit(0);
+}
+
+$data = [
+ 'organisation' => $_POST['organisation'],
+ 'anrede' => $_POST['anrede'],
+ 'anrede_briefkopf' => $_POST['anrede_briefkopf'],
+ 'titel' => $_POST['titel'],
+ 'vorname' => $_POST['vorname'],
+ 'nachname' => $_POST['nachname'],
+ 'strasse' => $_POST['strasse'],
+ 'adresszusatz' => $_POST['adresszusatz'],
+ 'plz' => $_POST['plz'],
+ 'ort' => $_POST['ort'],
+ 'betreff' => $_POST['subject'],
+ 'text' => $_POST['html'],
+ 'datum' => $_POST['date'],
+ 'data' => $_POST['data'],
+ 'fn' => $_POST['fn']
+];
+
+define('NUMERAL_SIGN', 'minus');
+define('NUMERAL_HUNDREDS_SUFFIX', 'hundert');
+define('NUMERAL_INFIX', 'und');
+
+$lNumeral = array('null', 'ein', 'zwei', 'drei', 'vier',
+ 'fünf', 'sechs', 'sieben', 'acht', 'neun',
+ 'zehn', 'elf', 'zwölf', 'dreizehn', 'vierzehn',
+ 'fünfzehn', 'sechzehn', 'siebzehn', 'achtzehn', 'neunzehn');
+
+$lTenner = array('', '', 'zwanzig', 'dreißig', 'vierzig',
+ 'fünfzig', 'sechzig', 'siebzig', 'achtzig', 'neunzig');
+
+$lGroupSuffix = array(array('s', ''),
+ array('tausend ', 'tausend '),
+ array('e Million ', ' Millionen '),
+ array('e Milliarde ', ' Milliarden '),
+ array('e Billion ', ' Billionen '),
+ array('e Billiarde ', ' Billiarden '),
+ array('e Trillion ', ' Trillionen '));
+
+function num2text($pNumber)
+{
+ global $lNumeral;
+ if ($pNumber == 0) {
+ return $lNumeral[0];
+ } elseif ($pNumber < 0) {
+ return NUMERAL_SIGN . ' ' . num2text_group(abs($pNumber));
+ } else {
+ return num2text_group($pNumber);
+ }
+}
+
+function num2text_group($pNumber, $pGroupLevel = 0)
+{
+ global $lNumeral, $lTenner, $lGroupSuffix;
+ if ($pNumber == 0) {
+ return '';
+ }
+ $lGroupNumber = $pNumber % 1000;
+ if ($lGroupNumber == 1) {
+ $lResult = $lNumeral[1] . $lGroupSuffix[$pGroupLevel][0];
+ } elseif ($lGroupNumber > 1) {
+ $lResult = '';
+ $lFirstDigit = floor($lGroupNumber / 100);
+ if ($lFirstDigit > 0) {
+ $lResult .= $lNumeral[$lFirstDigit] . NUMERAL_HUNDREDS_SUFFIX;
+ }
+ $lLastDigits = $lGroupNumber % 100;
+ $lSecondDigit = floor($lLastDigits / 10);
+ $lThirdDigit = $lLastDigits % 10;
+ if ($lLastDigits == 1) {
+ $lResult .= $lNumeral[1] . 's';
+ } elseif ($lLastDigits > 1 && $lLastDigits < 20) {
+ $lResult .= $lNumeral[$lLastDigits];
+ } elseif ($lLastDigits >= 20) {
+ if ($lThirdDigit > 0) {
+ $lResult .= $lNumeral[$lThirdDigit] . NUMERAL_INFIX;
+ }
+ $lResult .= $lTenner[$lSecondDigit];
+ }
+ $lResult .= $lGroupSuffix[$pGroupLevel][1];
+ }
+ return num2text_group(floor($pNumber / 1000), $pGroupLevel + 1) . $lResult;
+}
+
+try {
+ $ddata = json_decode($_POST['data']);
+ $data['data'] = $ddata;
+ $data['betrag'] = number_format($ddata->Betrag, 2, ",", ".");
+ $data['betrag_bs'] = num2text($ddata->Betrag) . ' Euro';
+ $data['betrag_datum'] = explode('-', $ddata->Geldeingang)[2] . '.' . explode('-', $ddata->Geldeingang)[1] . '.' . explode('-', $ddata->Geldeingang)[0];
+} catch (Exception $e) {
+ ;
+}
+
+$template = $_POST['template'];
+$template = 'hsrw.twig';
+
+$html = $twig->render($template, $data);
+
+$defaultConfig = (new Mpdf\Config\ConfigVariables())->getDefaults();
+$fontDirs = $defaultConfig['fontDir'];
+
+$defaultFontConfig = (new Mpdf\Config\FontVariables())->getDefaults();
+$fontData = $defaultFontConfig['fontdata'];
+
+$mpdf = new Mpdf\Mpdf([
+ 'tempDir' => __DIR__ . '/tmp',
+ 'format' => 'A4',
+]);
+
+//$mpdf->SetImportUse();
+$mpdf->useActiveForms = true;
+if (!strstr($template, 'zahlung')) {
+ $mpdf->SetProtection(array('print'));
+}
+$mpdf->WriteHTML($html);
+$mysqli->close();
+
+if (isset($_GET['test'])) {
+ header("Content-Type: application/pdf");
+ $mpdf->Output();
+ exit(0);
+}
+
+$fn = $_POST['fn'];
+
+mkdir("/var/www/uploads/" . $puid . "_");
+$file = "/var/www/uploads/" . $puid . "_/" . $fn . ".pdf";
+$mpdf->Output($file, \Mpdf\Output\Destination::FILE);
+
+if (isset($_GET['save'])) {
+ echo "1";
+ exit(0);
+}
+
+header("Content-Type: application/pdf");
+readfile($file);
+exit(0);
+
+?>
diff --git a/email.php b/email.php
new file mode 100644
index 0000000..5505957
--- /dev/null
+++ b/email.php
@@ -0,0 +1,708 @@
+<?php
+/*
+ * 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.
+ */
+?>
+<?
+/* vim: set ts=4 sw=4 et : */
+
+$_title = "E-Mail senden";
+
+require_once __DIR__ . "/check_auth.php";
+require_once __DIR__ . "/../includes/common.php";
+
+require_once __DIR__ . "/lookup.php";
+
+require_once __DIR__ . "/header.php";
+
+if (isset($_GET['demo'])) $_SESSION['demo'] = true;
+if (isset($_GET['demoself'])) $_SESSION['demoself'] = true;
+if (isset($_GET['nodemo'])) unset($_SESSION['demo']);
+if (isset($_GET['nodemoself'])) unset($_SESSION['demoself']);
+
+session_write_close();
+
+$num = 0;
+if ($_POST['ids'] != '')
+ $num = count(explode(',', $_POST['ids']));
+?>
+
+<div id="email_modal" style="display: none;" class="transparent_modal">
+ <button onclick="abortAll()" class="emergency_off">NOT-AUS</button>
+ <div class="sk-folding-cube hvcenter">
+ <div class="sk-cube1 sk-cube"></div>
+ <div class="sk-cube2 sk-cube"></div>
+ <div class="sk-cube4 sk-cube"></div>
+ <div class="sk-cube3 sk-cube"></div>
+ </div>
+</div>
+
+<h1>E-Mail senden
+<? if (!(isset($_POST['nocount']) && $_POST['nocount'])) { ?>
+ <? if ($num > 1) { ?>
+<span style="font-size: .5em;">f&uuml;r <?=$num?> Datens&auml;tze</span>
+ <? } else { ?>
+<span style="font-size: .5em;">f&uuml;r <?=$num?> Datensatz</span>
+ <? } ?>
+ <span style="font-size: .5em;" class="num_recps"></span>
+<? } ?>
+</h1>
+
+<? if ($_SESSION['demo']) { ?>
+<div style="border: 1px solid grey; padding: .2em; max-width: 500px; margin-bottom: .5em;">
+ <span style='color: darkred !important;'><b>Demo-Flag ist an</b> &mdash; es werden <b>keine E-Mails gesendet</b>, der Versand wird simuliert mit 3% Fehlerwahrscheinlichkeit.</span>
+ <br />
+ <button class="small" style="background: orange;" onclick="$.ajaxSetup({async:false}); $.post('/db/main/nodemo.php'); location.reload(); return false;">Demo-Flag entfernen</button>
+</div>
+<? } ?>
+
+<? if ($_SESSION['demoself']) { ?>
+<div style="border: 1px solid grey; padding: .2em; max-width: 500px; margin-bottom: .5em;">
+ <span style='color: darkred !important;'><b>Demo-Self-Flag ist an</b> &mdash; Der E-Mail-Empfänger (To) wird beim Versand durch den E-Mail-Absender (From) ersetzt. Cc und Bcc werden ignoriert.</span>
+ <br />
+ <button class="small" style="background: orange;" onclick="$.ajaxSetup({async:false}); $.post('/db/main/nodemoself.php'); location.reload(); return false;">Demo-Self-Flag entfernen</button>
+</div>
+<? } ?>
+
+<?
+ $sql = "
+SELECT Personen.Email
+FROM Personen_Organisationen
+INNER JOIN Personen ON Personen_Organisationen.Person = Personen.ID
+WHERE Personen_Organisationen.Organisation=526 AND Personen.Nachname=?
+LIMIT 1
+";
+ $stmt = $mysqli->prepare($sql);
+ $stmt->bind_param('s', $_SESSION['auth_user']);
+ $stmt->bind_result($semail);
+ $stmt->execute();
+ $stmt->fetch();
+ $stmt->reset();
+?>
+
+<table border="0" style="display: inline-block; max-width: 460px;">
+ <tr>
+ <td>Absender</td>
+ <td><input style="width: 25em;" class="mand" type="text" name="email_from" placeholder="absender@hochschule-rhein-waal.de" value="<?=$semail?>"></td>
+ <td></td>
+ </tr>
+ <tr>
+ <td>ID/To<sup><small>Preview</small></sup></td>
+ <td>
+ <input type="text" style="width: 5em;" disabled name="email_id" value="">
+ <input type="text" style="width: 19em;" disabled name="email_to" value="">
+ </td>
+ </tr>
+ <tr>
+ <td>Cc</td>
+ <td>
+ <input type="text" style="width: 25em;" name="email_cc" placeholder="erster@kontakt.cc, zweiter@kontakt.cc">
+ <i style="cursor: pointer;" onclick="$('#cc_info').toggle();" class="fas fa-info-circle"></i>
+ <p id="cc_info" style="display: none;" class="explain">Bei Cc und Bcc sind jeweils mehrere Adressaten m&ouml;glich. Durch Komma trennen.</p>
+ </td>
+ </tr>
+ <tr>
+ <td>Bcc</td>
+ <td><input type="text" style="width: 25em;" name="email_bcc" placeholder="bcc@hochschule-rhein-waal.de"></td>
+ </tr>
+ <tr>
+ <td>Betreff</td>
+ <td>
+ <input class="mand" style="width: 25em;" type="text" name="email_subject" placeholder="Betreff">
+ <i style="cursor: pointer;" onclick="$('#betreff_info').toggle();" class="fas fa-info-circle"></i>
+ <p id="betreff_info" style="display: none;" class="explain">Umlaute und Emojis k&ouml;nnen nun auch im Betreff verwendet werden.</p>
+ </td>
+ </tr>
+</table>
+
+<div style="display: inline-block; margin-left: .5em; vertical-align: top;">
+ <button onclick="return sendAll();" class="medium" style="width: 12em;"><i class="fas fa-mail-bulk"></i> an alle senden</button><br />
+<? if (!isset($_SESSION['singleemail'])) { ?>
+ <button onclick="return sendOne();" class="medium" style="width: 12em;"><i class="fas fa-envelope"></i> an diesen senden</button>
+<? } ?>
+
+ <div style="color: darkorange; margin-top: .5em;">
+<? if (!$_SESSION['demo']) { ?>
+ <button class="small" style="background: orange;" onclick="$.ajaxSetup({async:false}); $.post('/db/main/demo.php'); location.reload(); return false;">Demo-Flag setzen</button><br />
+<? } ?>
+<? if (!$_SESSION['demoself']) { ?>
+ <button class="small" style="background: orange;" onclick="$.ajaxSetup({async:false}); $.post('/db/main/demoself.php'); location.reload(); return false;">Demo-Self-Flag setzen</button><br />
+<? } ?>
+<? if (!$_SESSION['singleemail']) { ?>
+ <button class="small" style="background: lightcoral;" onclick="$.ajaxSetup({async:false}); $.post('/db/main/singleemail.php'); location.reload(); return false;">Auf Einzel-E-Mail schalten</button><br />
+<? } ?>
+
+ Letzter Status:
+ <p id="email_status"></p>
+ </div>
+</div>
+
+<div style="display: inline-block; margin-left: 1em; vertical-align: top;">
+ <b>Gespeicherte Templates <i style="cursor: pointer;" onclick="$('#templater_info').toggle();" class="fas fa-info-circle"></i></b>
+ <p id="templater_info" style="display: none;" class="explain">Nicht jedes Template funktioniert in jeder Abfrage. Manche Abfragen haben spezifische Payloads hinterlegt (z.B. Eventmanagement). Bestimmte Felder existieren z.B. auch nur in Bewerbungsdatens&auml;tzen. Daher bitte zun&auml;chst stichprobenartig durch die Vorschau-Funktion pr&uuml;fen, ob die erwarteten Ergebnisse erzeugt werden.<br />Sollte der Templater von alleine aus erkennen, dass spezifische Payloads n&ouml;tig sind, warnt er. Dies ist jedoch aus technischen Gr&uuml;nden nicht immer m&ouml;glich.<br />Anh&auml;nge werden grunds&auml;tzlich <b>nicht</b> zusammen mit dem Template gespeichert und auch nicht beim Laden eines anderen Templates zur&uuml;ckgesetzt.</p>
+
+ <div style="color: darkorange; margin-top: .1em;">
+<?
+ $sql = "
+SELECT id, name, text, subject, `from`, cc, bcc, ts, tags, tid as fav
+FROM email_templates
+LEFT JOIN email_templates_fav ON email_templates.id = email_templates_fav.tid AND email_templates_fav.userid = ?
+WHERE active = 1
+ORDER BY fav DESC, name ASC";
+ $stmt = $mysqli->prepare($sql);
+ $stmt->bind_param('i', $_SESSION['auth_userid']);
+ $stmt->bind_result($id, $name, $text, $subject, $from, $cc, $bcc, $ts, $tags, $fav);
+ $stmt->execute();
+?>
+ <select id="email_template">
+ <option data-id="" data-name="" data-text="" data-from="" data-cc="" data-bcc="" data-subject="" data-tags="" value="" selected></option>
+<?
+ $templates = [];
+ while ($stmt->fetch()) {
+ $templates[$id] = ['name' => $name, 'text' => $text, 'ts' => $ts];
+?>
+ <option data-id="<?=$id?>"
+ data-name="<?=str_replace("\"", "&qout;", $name)?>"
+ data-from="<?=str_replace("\"", "&quot;", $from)?>"
+ data-cc="<?=str_replace("\"", "&quot;", $cc)?>"
+ data-bcc="<?=str_replace("\"", "&quot;", $bcc)?>"
+ data-subject="<?=str_replace("\"", "&quot;", $subject)?>"
+ data-text="<?=str_replace("\"", "&quot;", $text)?>"
+ data-tags="<?=str_replace("\"", "&quot;", $tags)?>"
+ value="<?=$id?>"><?=$fav?'★ ':''?><?=$name?> (ID <?=$id?>; <?=$ts?>)</option>
+<?
+ }
+?>
+ </select>
+ <div style="display: block; margin-top: .2em;">
+ <button class="small" onclick="return loadEmailTemplate();"><i class="fas fa-upload"></i> Laden</button>
+ <button class="small" onclick="return saveEmailTemplate();" style="background: orange"><i class="fas fa-download"></i> &Uuml;berschreiben</button>
+ <button class="small" onclick="return newEmailTemplate();"><i class="far fa-file"></i> Neu</button>
+ <button class="small" onclick="return renameEmailTemplate();"><i class="fas fa-pencil-alt"></i> Umbenennen</button>
+ <button class="small" onclick="return delEmailTemplate();" style="background: red;"><i class="fas fa-trash"></i> L&ouml;schen</button>
+ </div>
+ <label style="font-family: monospace;" for="etfiltertags">Filter Tags</label>&nbsp;<input id="etfiltertags" name="etfiltertags" placeholder="Tags" /><br />
+ <label style="font-family: monospace;" for="etfiltername">Filter Name</label>&nbsp;<input id="etfiltername" name="etfiltername" placeholder="Name" /><br />
+ <label style="font-family: monospace;" for="etfilterid">Filter ID&nbsp;&nbsp;</label>&nbsp;<input id="etfilterid" name="etfilterid" placeholder="ID" /><br />
+ <a id="manageets" href="/db/templates/emails/">Templates verwalten</a>
+ <a id="manageet" style="display: none;" href=""></a><br />
+ <div id="email_template_warnings" style="display: block; margin-top: .2em;">
+ </div>
+<?
+ $stmt->reset();
+?>
+
+ </div>
+</div>
+
+<div id="attachments" style="display: inline-block; margin-left: 0; vertical-align: top;">
+ <b>Anh&auml;nge</b>
+ <small>(PDF, Word*, Excel*, PowerPoint*, JPG, PNG, ZIP)</small>
+ <i style="cursor: pointer;" onclick="$('#office_info').toggle();" class="fas fa-info-circle"></i>
+ <p id="office_info" style="display: none;" class="explain">
+ Office-Formate kommen ggf. nicht durch Firewalls und serverseitig installierte Spamfilter. Wenn m&ouml;glich PDF verwenden.<br />
+ Anh&auml;nge m&uuml;ssen zuvor im daf&uuml;r vorgesehenen Ordner im CMS hochgeladen werden.
+ </p>
+
+<?
+ for ($i = 1; $i < 13; $i++) {
+?>
+ <div style="clear: both;"></div>
+<?
+ $sql = "
+SELECT hash, name, mime
+FROM pages_files
+WHERE
+(
+ mime='application/pdf'
+ OR mime='image/jpeg'
+ OR mime='image/png'
+ OR mime='application/vnd.openxmlformats-officedocument.wordprocessingml.document' OR mime='application/msword'
+ OR mime='application/vnd.ms-excel' OR mime='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
+ OR mime='application/vnd.ms-powerpoint' OR mime='application/vnd.openxmlformats-officedocument.presentationml.presentation'
+ OR mime='application/zip'
+)
+ AND sid=4
+ORDER BY name
+";
+ $stmt = $mysqli->prepare($sql);
+ $stmt->bind_result($fid, $fname, $fmime);
+ $stmt->execute();
+?>
+ <select class="attachment" id="attachment<?=$i?>" <? if ($i != 1) { ?> style="display: none;" <? } ?>>
+ <option value="" selected></option>
+<?
+ while ($stmt->fetch()) {
+?>
+ <option value="<?=$fid?>" data-mime="<?=$fmime?>"><?=$fname?></option>
+<?
+ }
+ $stmt->reset();
+
+ $sql = "SELECT name, description FROM pdf_documents ORDER BY name";
+ $stmt = $mysqli->prepare($sql);
+ $stmt->bind_result($fname, $fdesc);
+ $stmt->execute();
+ while ($stmt->fetch()) {
+?>
+ <option value="~<?=$fname?>" data-mime="application/pdf">~PDF: <?=$fname?> (<?=$fdesc?>)</option>
+<?
+ }
+ $stmt->reset();
+?>
+ </select>
+<?
+ }
+?>
+</div>
+
+<script>
+<? minStart(); ?>
+
+$('#email_template').on('change', function () {
+ $('#email_template').css('background', 'white');
+});
+
+$('select.attachment').on('change', function () {
+ if ($(this).val() == '')
+ $(this).nextAll('select.attachment').val('').hide();
+ else
+ $(this).nextAll('select.attachment').first().show();
+});
+
+$(document).ready(function () {
+ quill.on('text-change', function () {
+ $('#email_template').css('background', 'lightgrey');
+ });
+
+ setTimeout(function () {
+ var tid = localStorage.getItem('email_template');
+ if (tid !== '' && tid > 0) {
+ $('select#email_template').val(tid);
+ localStorage.removeItem('email_template');
+ setTimeout(function () {
+ loadEmailTemplate();
+ }, 100);
+ }
+ }, 200);
+});
+
+function newEmailTemplate() {
+ var n = prompt('Name des neuen Templates:', '');
+ if (!n) return;
+
+ var text = $("#editor .ql-editor").html();
+ var from = $('input[name=email_from]').val();
+ var cc = $('input[name=email_cc]').val();
+ var bcc = $('input[name=email_bcc]').val();
+ var subject = $('input[name=email_subject').val();
+
+ $.ajaxSetup({async:false});
+ var tid = 0;
+ $.post('/db/main/addtemplate.php', {
+ 'name': n,
+ 'text': text,
+ 'from': from,
+ 'cc': cc,
+ 'bcc': bcc,
+ 'subject': escape(subject),
+ 'email': true
+ }, function (rv) { tid = rv; });
+
+ localStorage.setItem('email_template', tid);
+ alert(unescape('Neues Template ID ' + tid + ' mit den aktuellen Werten wurde erzeugt.\r\n\r\nDie Seite lädt nun neu.'));
+ location.reload();
+}
+
+function renameEmailTemplate() {
+ var id = $('#email_template :selected').attr('data-id');
+ var on = $('#email_template :selected').attr('data-name');
+ var n = prompt('Name des Templates:', on);
+ if (!n) return;
+
+ $.ajaxSetup({async:false});
+ $.post('/db/main/rentemplate.php', {
+ 'id': id,
+ 'name': n,
+ 'email': true
+ });
+
+ $('#email_template :selected').attr('data-name', n);
+ $('#email_template :selected').text(n + ' (ID ' + id + ')');
+}
+
+function delEmailTemplate() {
+ var id = $('#email_template').val();
+ var v = $('#email_template :selected').text();
+ if (!v) return;
+
+ if (confirm(unescape('Wirklich Template\n "' + v + '"\nunwiderruflich verwerfen?'))) {
+ $.ajaxSetup({async:false});
+ $.post('/db/main/deltemplate.php', {
+ 'id': id,
+ 'email': true
+ });
+ $('#email_template :selected').remove();
+ $('#email_template').css('background', 'red');
+ $('#manageet').hide();
+ }
+}
+
+function loadEmailTemplate() {
+ var text = $('#email_template :selected').attr('data-text');
+
+ $("#editor .ql-editor")[0].innerHTML = $('#email_template :selected').attr('data-text');
+ $("#editor .ql-editor").html($('#email_template :selected').attr('data-text').replace(/<br>/g, '<br>\n'));
+ $('input[name=email_from]').val($('#email_template :selected').attr('data-from'));
+ $('input[name=email_cc]').val($('#email_template :selected').attr('data-cc'));
+ $('input[name=email_bcc]').val($('#email_template :selected').attr('data-bcc'));
+ $('input[name=email_subject]').val(unescape($('#email_template :selected').attr('data-subject')));
+
+ setTimeout(function () {
+ $('#email_template').css('background', 'lightgreen');
+ }, 200);
+
+ var w = "";
+ if (text.match(/{{[^}]*event/)) {
+ w += "Template enthaelt Auszeichnungen fuer Events. Abfrage aus dem Eventmanagement noetig.<br />";
+ }
+ if (text.match(/{{[^}]*stiplist/)) {
+ w += "Template enthaelt Auszeichnungen fuer Stipendiatenliste. Spezieller Payload noetig.<br />";
+ }
+ if (text.match(/{{[^}]*stipvergabe/)) {
+ w += "Template enthaelt Auszeichnungen fuer Stipvergabe. Spezieller Payload noetig.<br />";
+ }
+ if (text.match(/{{[^}]*my/)) {
+ w += "Template enthaelt Auszeichnungen fuer pers. Bereich. Abfrage aus Bewerbungen oder Stipendien noetig.<br />";
+ }
+ if (text.match(/{{[^}]*mylue/)) {
+ w += "Template enthaelt Auszeichnungen fuer LUe. Abfrage aus Leistungsueberpruefung noetig. Abfrage aus Stipendien generiert falsche URL.<br />";
+ }
+ if (text.match(/{{[^}]*eventlist/)) {
+ w += "Template enthaelt Auszeichnungen fuer Ideelle Foerderung. Kombinationsabfrage aus mehreren Events noetig.<br />";
+ }
+ if (text.match(/{{[^}]*d\['s2'\]/) || text.match(/{{[^}]*d\.s2/)) {
+ w += "Template enthaelt Auszeichnungen fuer Bewerbungsdatensaetze. Abfrage aus Bewerbung noetig.<br />";
+ }
+ if (text.match(/{{[^}]*ip /)) {
+ w += "Template verweist auf einen Independent Payload. Spezielle Abfrage noetig.<br />";
+ }
+ if (text.match(/{{[^}]*filter/)) {
+ w += "Template enthaelt Filteranweisungen. Sehr wahrscheinlich spezielle Abfrage noetig.<br />";
+ }
+ if (text.match(/┌/)) {
+ w += "Template enthaelt rekursives Templating. Mit an Sicherheit grenzender Wahrscheinlichkeit spezielle Abfrage noetig.<br />";
+ }
+ $('#email_template_warnings').html(w);
+
+ $('#manageet').attr('href', '/db/templates/email/' + $('#email_template :selected').attr('data-id')).html('ID ' + $('#email_template :selected').attr('data-id') + ' verwalten').show();
+}
+
+function saveEmailTemplate() {
+ var text = $("#editor .ql-editor").html();
+ var from = $('input[name=email_from]').val();
+ var cc = $('input[name=email_cc]').val();
+ var bcc = $('input[name=email_bcc]').val();
+ var subject = $('input[name=email_subject').val();
+ var id = $('#email_template').val();
+
+ $('#email_template :selected').attr('data-text', text);
+ $('#email_template :selected').attr('data-from', from);
+ $('#email_template :selected').attr('data-cc', cc);
+ $('#email_template :selected').attr('data-bcc', bcc);
+ $('#email_template :selected').attr('data-subject', escape(subject));
+ $('#email_template :selected').text($('#email_template :selected').attr('data-name') + ' (ID ' + $('#email_template :selected').attr('data-id') + ')');
+
+ $.ajaxSetup({async:false});
+ $.post('/db/main/updtemplate.php', {
+ 'id': id,
+ 'text': text,
+ 'from': from,
+ 'cc': cc,
+ 'bcc': bcc,
+ 'subject': escape(subject),
+ 'email': true
+ });
+
+ setTimeout(function () {
+ $('#email_template').css('background', 'lightgreen');
+ $('#manageet').attr('href', '/db/templates/email/' + $('#email_template :selected').attr('data-id')).html('ID ' + $('#email_template :selected').attr('data-id') + ' verwalten').show();
+ }, 200);
+}
+
+<? minEnd(); ?>
+</script>
+
+<?
+$sql = $_POST['sql'];
+$post_sql = $_POST['postsql'];
+$payload_sql = $_POST['payload_sql'];
+$payload_sql_order = $_POST['payload_sql_order'];
+$independent_payload_sql = $_POST['independent_payload_sql'];
+$independent_payload_sql_order = $_POST['independent_payload_sql_order'];
+$isemail = true;
+require_once __DIR__ . "/templater_common.php";
+?>
+
+<script>
+<? minStart(); ?>
+
+function sendOne(skip) {
+ if (typeof skip !== 'boolean')
+ skip = false;
+
+ if (typeof pivot.__skip !== 'undefined') {
+ if (pivot.__skip) {
+ delete pivot.__done;
+ delete pivot.__failed;
+ data[pivot.index].__skip = true;
+ redoRows($("tr[data-id='" + pivot.uid + "']")[0], 0, 0, 0, pivot.index);
+ redoRows($("tr[data-id='" + pivot.ID + "']")[0], 0, 0, 0, pivot.index);
+ redoRows($("tr[data-id='" + pivot.id + "']")[0], 0, 0, 0, pivot.index);
+ return true;
+ }
+ }
+
+ if (typeof pivot.__done !== 'undefined') {
+ if (!confirm(unescape('An diese Adresse wurde schon gesendet. Nochmal senden?')))
+ return;
+ }
+
+ var from = $('input[name=email_from]').val();
+ var to = $('input[name=email_to]').val();
+ var cc = $('input[name=email_cc]').val();
+ var bcc = $('input[name=email_bcc]').val();
+ var subject = $('input[name=email_subject]').val();
+ var html = $('#preview').html();
+ if ($('textarea.htmlcontainer').val().length > 10) {
+ html = $('textarea.htmlcontainer').val();
+ }
+
+ var persid = null;
+ var stipid = null;
+ var uid = null;
+
+ if (typeof pivot.ID !== 'undefined') persid = pivot.ID;
+ if (typeof pivot.id !== 'undefined') persid = pivot.id;
+ if (typeof pivot.PersID !== 'undefined') persid = pivot.PersID;
+ if (typeof pivot.persid !== 'undefined') persid = pivot.persid;
+ if (typeof pivot.StipID !== 'undefined') stipid = pivot.StipID;
+ if (typeof pivot.stipid !== 'undefined') stipid = pivot.stipid;
+ if (typeof pivot.UID !== 'undefined') uid = pivot.UID;
+ if (typeof pivot.uid !== 'undefined') uid = pivot.uid;
+
+ var attachments = [];
+ $('#attachments select.attachment :selected').each(function () {
+ if (!$(this).val()) return;
+ if ($(this).val().charAt(0) === '~') {
+ attachments.push('~' + uid + '_/' + $(this).val().substring(1) + '.pdf');
+ } else {
+ attachments.push($(this).val());
+ }
+ });
+ var attachments_fn = [];
+ $('#attachments select.attachment :selected').each(function () { if (!$(this).text()) return; attachments_fn.push($(this).text()); });
+ var attachments_mime = [];
+ $('#attachments select.attachment :selected').each(function () { if (!$(this).attr('data-mime')) return; attachments_mime.push($(this).attr('data-mime')); });
+
+ var noerror = false;
+ var response = "";
+ $.ajaxSetup({async:false});
+ $.ajax({
+ type: 'POST',
+ url: '/db/main/sendmail.php',
+ data: {
+ 'from': from,
+ 'to': to,
+ 'cc': cc,
+ 'bcc' : bcc,
+ 'html' : html,
+ 'subject': subject,
+ 'persid': persid,
+ 'stipid': stipid,
+ 'uid': uid,
+ 'attachments': attachments,
+ 'attachments_fn': attachments_fn,
+ 'attachments_mime': attachments_mime
+ },
+ success: function (d, s, x) {
+ if (d == 1) {
+ noerror = true;
+ response = x.responseText;
+ }
+ if (d == 2) {
+ response = "Gewählter Anhang nicht auffindbar.";
+ }
+ if (d == 3) {
+ response = "Es gibt einen Personeneintrag mit der E-Mail-Adresse (alle Felder), in dem 'wünscht keine E-Mails' gesetzt ist.";
+ }
+ }
+ });
+
+ if (noerror && response == '1') {
+ pivot.__done = true;
+ delete pivot.__failed;
+
+ redoRows($("tr[data-id='" + pivot.uid + "']")[0], 0, 0, 0, pivot.index);
+ redoRows($("tr[data-id='" + pivot.ID + "']")[0], 0, 0, 0, pivot.index);
+ redoRows($("tr[data-id='" + pivot.id + "']")[0], 0, 0, 0, pivot.index);
+
+ if (!skip) alert('E-Mail an ' + to + ' erfolgreich gesendet.');
+
+ $('#email_status').html('[#' + (count_s + count_f + 1) + '] E-Mail erfolgreich: ' + to);
+ return true;
+ }
+
+ pivot.__failed = true;
+ delete pivot.__done;
+
+ redoRows($("tr[data-id='" + pivot.uid + "']")[0], 0, 0, 0, pivot.index);
+ redoRows($("tr[data-id='" + pivot.ID + "']")[0], 0, 0, 0, pivot.index);
+ redoRows($("tr[data-id='" + pivot.id + "']")[0], 0, 0, 0, pivot.index);
+
+ alert('Fehler beim Senden an ' + to + ': ' + response);
+ $('#email_status').html('Fehler: ' + to);
+ return false;
+}
+
+function waitASec(time) {
+ $.ajaxSetup({async:false});
+ $.post('/db/main/delay.php', {
+ time: time
+ });
+}
+
+function sendAllDone() {
+ $('#email_modal').hide();
+ window.scrollTo(0, 0);
+ alert(unescape('Fertig. Erfolg für ' + count_s + ' Empfänger.\nFehlgeschlagen bei ' + count_f + '.'));
+ $('#email_status').html('Erfolg: ' + count_s + ', Fehler: ' + count_f);
+}
+
+function sendAllLoop() {
+ if (typeof window.emergencyOff !== 'undefined') {
+ sendAllDone();
+ delete window.emergencyOff;
+ return;
+ }
+
+ if (typeof pivot.__done === 'undefined') {
+ $('#email_status').html('Sende an: ' + pivot['email']);
+
+ if (sendOne(true)) count_s++;
+ else count_f++;
+
+ pivot_iter++;
+ if (pivot_iter < data.length) {
+ pivot = data[pivot_iter];
+ bP();
+ setTimeout(sendAllLoop, 60);
+ return;
+ }
+
+ setTimeout(sendAllDone, 60);
+ return;
+ }
+
+ pivot_iter++;
+ if (pivot_iter < data.length) {
+ pivot = data[pivot_iter];
+ bP();
+ setTimeout(sendAllLoop, 10);
+ return;
+ }
+
+ setTimeout(sendAllDone, 80);
+ return;
+}
+
+function sendAll() {
+ if (!confirm(unescape('Wirklich E-Mail an ' + $.grep(data, function (e) { return (typeof e.__done === 'undefined'); }).length + ' Empfänger senden?')))
+ return;
+
+ count_s = 0;
+ count_f = 0;
+ pivot = data[0];
+ pivot_iter = 0;
+ bP();
+
+ window.scrollTo(0, 0);
+ $('#email_modal').show();
+ setTimeout(sendAllLoop, 100);
+}
+
+function abortAll() {
+ window.emergencyOff = true;
+}
+
+
+function etFilter() {
+ $('#email_template option').removeAttr('selected');
+ $('#email_template option').prop('selected', false);
+
+ t = $('#etfiltertags').val();
+ t = t.trim();
+ t.replace(/\s+/g, ' ');
+
+ n = $('#etfiltername').val();
+ n = n.trim();
+
+ id = $('#etfilterid').val();
+ id = id.trim();
+
+ $('#email_template option').each(function (i, o) {
+ var show = true;
+ var it = $(this);
+
+ t.split(' ').forEach(function (e) {
+ if (!it.attr('data-tags').toLowerCase().includes(e.toLowerCase())) {
+ show = false;
+ }
+ });
+
+ if (n && n.length > 0) {
+ if (!$(this).attr('data-name').toLowerCase().includes(n.toLowerCase())) {
+ show = false;
+ }
+ }
+
+ if (parseInt(id) > 0) {
+ if ($(this).attr('data-id') != id) {
+ show = false;
+ }
+ }
+
+ if (show) {
+ $(this).show();
+ } else {
+ $(this).hide();
+ }
+ });
+}
+
+$(document).ready(function () {
+ $('#etfiltertags').on('change input keyup', function (e) {
+ etFilter();
+ });
+ $('#etfiltername').on('change input keyup', function (e) {
+ etFilter();
+ });
+ $('#etfilterid').on('change input keyup', function (e) {
+ etFilter();
+ });
+});
+
+<? minEnd(); ?>
+</script>
+
+<?
+include_once __DIR__ . "/footer.php";
+?>
diff --git a/emailhtml.php b/emailhtml.php
new file mode 100644
index 0000000..0b44ec3
--- /dev/null
+++ b/emailhtml.php
@@ -0,0 +1,454 @@
+<?php
+/*
+ * 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.
+ */
+?>
+<?php
+/* vim: set ts=4 sw=4 et : */
+
+$_title = "E-Mail senden";
+
+require_once __DIR__ . "/check_auth.php";
+require_once __DIR__ . "/../includes/common.php";
+
+require_once "./config.php";
+require_once __DIR__ . "/lookup.php";
+
+require_once __DIR__ . "/header.php";
+
+if (isset($_GET['demo'])) $_SESSION['demo'] = true;
+
+$num = 0;
+if ($_POST['ids'] != '')
+ $num = count(explode(',', $_POST['ids']));
+?>
+
+<div id="email_modal" style="display: none;" class="transparent_modal">
+ <button onclick="abortAll()" class="emergency_off">NOT-AUS</button>
+ <div class="sk-folding-cube hvcenter">
+ <div class="sk-cube1 sk-cube"></div>
+ <div class="sk-cube2 sk-cube"></div>
+ <div class="sk-cube4 sk-cube"></div>
+ <div class="sk-cube3 sk-cube"></div>
+ </div>
+</div>
+
+<h1>E-Mail senden</h1>
+
+<? if ($_SESSION['demo']) { ?>
+<p>Demo-Flag ist an</p>
+<? } ?>
+
+<table border="0" style="display: inline-block;">
+<? if (!(isset($_POST['nocount']) && $_POST['nocount'])) { ?>
+ <tr>
+ <td colspan="2">
+ an <?=$num?> Empfänger
+ </td>
+ </tr>
+<? } ?>
+ <tr>
+ <td>Absender</td>
+ <td><input class="mand" type="text" name="email_from" placeholder="absender@demo.de"></td>
+ </tr>
+ <tr>
+ <td>To (Preview)</td>
+ <td><input type="text" disabled name="email_to" value=""></td>
+ </tr>
+ <tr>
+ <td>ID (Preview)</td>
+ <td><input type="text" disabled name="email_id" value=""></td>
+ </tr>
+ <tr>
+ <td>Cc</td>
+ <td><input type="text" name="email_cc" placeholder="erster@kontakt.cc, zweiter@kontakt.cc"></td>
+ </tr>
+ <tr>
+ <td>Bcc</td>
+ <td><input type="text" name="email_bcc" placeholder="bcc@demo.de"></td>
+ </tr>
+ <tr>
+ <td>Betreff</td>
+ <td><input class="mand" type="text" name="email_subject" placeholder="Betreff"></td>
+ </tr>
+</table>
+
+<div style="display: inline-block; margin-left: 1em; vertical-align: top;">
+ <button onclick="return sendAll();" class="medium" style="width: 12em;"><i class="fas fa-mail-bulk"></i> an alle senden</button><br />
+ <button onclick="return sendOne();" class="medium" style="width: 12em;"><i class="fas fa-envelope"></i> an diesen senden</button>
+
+ <div style="color: darkorange; margin-top: .5em;">
+ Letzter Status:
+ <p id="email_status"></p>
+ </div>
+</div>
+
+<div style="display: inline-block; margin-left: 1em; vertical-align: top;">
+ <b>Gespeicherte Templates</b>
+ <div style="color: darkorange; margin-top: .5em;">
+<?
+ $sql = "SELECT id, name, text, subject, `from`, cc, bcc, ts FROM email_templates ORDER BY name";
+ $stmt = $mysqli->prepare($sql);
+ $stmt->bind_result($id, $name, $text, $subject, $from, $cc, $bcc, $ts);
+ $stmt->execute();
+?>
+ <select id="email_template">
+ <option data-text="" data-from="" data-cc="" data-bcc="" data-subject="" value="" selected></option>
+<?
+ $templates = [];
+ while ($stmt->fetch()) {
+ $templates[$id] = ['name' => $name, 'text' => $text, 'ts' => $ts];
+?>
+ <option data-id="<?=$id?>"
+ data-name="<?=str_replace("\"", "&qout;", $name)?>"
+ data-from="<?=str_replace("\"", "&quot;", $from)?>"
+ data-cc="<?=str_replace("\"", "&quot;", $cc)?>"
+ data-bcc="<?=str_replace("\"", "&quot;", $bcc)?>"
+ data-subject="<?=str_replace("\"", "&quot;", $subject)?>"
+ data-text="<?=str_replace("\"", "&quot;", $text)?>"
+ value="<?=$id?>"><?=$name?> (ID <?=$id?>; <?=$ts?>)</option>
+<?
+ }
+?>
+ </select>
+ <br />
+ <br />
+ <button class="small" onclick="return loadEmailTemplate();"><i class="fas fa-upload"></i> Laden</button>
+ <button class="small" onclick="return saveEmailTemplate();" style="background: orange"><i class="fas fa-download"></i> &Uuml;berschreiben</button>
+ <button class="small" onclick="return newEmailTemplate();"><i class="far fa-file"></i> Neu</button>
+ <button class="small" onclick="return renameEmailTemplate();"><i class="fas fa-pencil-alt"></i> Umbenennen</button>
+ <button class="small" onclick="return delEmailTemplate();" style="background: red;"><i class="fas fa-trash"></i> L&ouml;schen</button>
+<?
+ $stmt->reset();
+?>
+
+ </div>
+</div>
+
+<div style="display: inline-block; margin-left: 1em; vertical-align: top;">
+ <b>Anhang</b>
+<?
+ $sql = "SELECT hash, name FROM pages_files WHERE mime='application/pdf' ORDER BY name";
+ $stmt = $mysqli->prepare($sql);
+ $stmt->bind_result($fid, $fname);
+ $stmt->execute();
+?>
+ <select id="attachment">
+ <option value="" selected></option>
+<?
+ while ($stmt->fetch()) {
+?>
+ <option value="<?=$fid?>"><?=$fname?></option>
+<?
+ }
+ $stmt->reset();
+?>
+ </select>
+</div>
+
+<script>
+<?php minStart(); ?>
+
+$('#email_template').on('change', function () {
+ $('#email_template').css('background', 'white');
+});
+
+$(document).ready(function () {
+quill.on('text-change', function () {
+ $('#email_template').css('background', 'lightgrey');
+});
+});
+
+function newEmailTemplate() {
+ var n = prompt('Name des neuen Templates:', '');
+ if (!n) return;
+
+ var text = $("#editor .ql-editor").html();
+ var from = $('input[name=email_from]').val();
+ var cc = $('input[name=email_cc]').val();
+ var bcc = $('input[name=email_bcc]').val();
+ var subject = $('input[name=email_subject').val();
+
+ $.ajaxSetup({async:false});
+ $.post('/db/main/addtemplate.php', {
+ 'name': n,
+ 'text': text,
+ 'from': from,
+ 'cc': cc,
+ 'bcc': bcc,
+ 'subject': subject,
+ 'email': true
+ });
+ alert(unescape('Seite neuladen, damit neues Template im Dropdown erscheint.\nIn dem neuen Template wurden die aktuellen Werte gespeichert.'));
+}
+
+function renameEmailTemplate() {
+ var id = $('#email_template :selected').attr('data-id');
+ var on = $('#email_template :selected').attr('data-name');
+ var n = prompt('Name des Templates:', on);
+ if (!n) return;
+
+ $.ajaxSetup({async:false});
+ $.post('/db/main/rentemplate.php', {
+ 'id': id,
+ 'name': n,
+ 'email': true
+ });
+
+ $('#email_template :selected').attr('data-name', n);
+ $('#email_template :selected').text(n + ' (ID ' + id + ')');
+}
+
+function delEmailTemplate() {
+ var id = $('#email_template').val();
+ var v = $('#email_template :selected').text();
+ if (!v) return;
+
+ if (confirm(unescape('Wirklich Template\n "' + v + '"\nunwiderruflich verwerfen?'))) {
+ $.ajaxSetup({async:false});
+ $.post('/db/main/deltemplate.php', {
+ 'id': id,
+ 'email': true
+ });
+ $('#email_template :selected').remove();
+ $('#email_template').css('background', 'red');
+ }
+}
+
+function loadEmailTemplate() {
+ $("#editor .ql-editor")[0].innerHTML = $('#email_template :selected').attr('data-text');
+ $("#editor .ql-editor").html($('#email_template :selected').attr('data-text').replace(/<br>/g, '<br>\n'));
+ $('input[name=email_from]').val($('#email_template :selected').attr('data-from'));
+ $('input[name=email_cc]').val($('#email_template :selected').attr('data-cc'));
+ $('input[name=email_bcc]').val($('#email_template :selected').attr('data-bcc'));
+ $('input[name=email_subject]').val($('#email_template :selected').attr('data-subject'));
+
+ setTimeout(function () {
+ $('#email_template').css('background', 'lightgreen');
+ }, 200);
+}
+
+function saveEmailTemplate() {
+ var text = $("#editor .ql-editor").html();
+ var from = $('input[name=email_from]').val();
+ var cc = $('input[name=email_cc]').val();
+ var bcc = $('input[name=email_bcc]').val();
+ var subject = $('input[name=email_subject').val();
+ var id = $('#email_template').val();
+
+ $('#email_template :selected').attr('data-text', text);
+ $('#email_template :selected').attr('data-from', from);
+ $('#email_template :selected').attr('data-cc', cc);
+ $('#email_template :selected').attr('data-bcc', bcc);
+ $('#email_template :selected').attr('data-subject', subject);
+ $('#email_template :selected').text($('#email_template :selected').attr('data-name') + ' (ID ' + $('#email_template :selected').attr('data-id') + ')');
+
+ $.ajaxSetup({async:false});
+ $.post('/db/main/updtemplate.php', {
+ 'id': id,
+ 'text': text,
+ 'from': from,
+ 'cc': cc,
+ 'bcc': bcc,
+ 'subject': subject,
+ 'email': true
+ });
+
+ setTimeout(function () {
+ $('#email_template').css('background', 'lightgreen');
+ }, 200);
+}
+
+<?php minEnd(); ?>
+</script>
+
+<?
+$sql = $_POST['sql'];
+$payload_sql = $_POST['payload_sql'];
+$payload_sql_order = $_POST['payload_sql_order'];
+$independent_payload_sql = $_POST['independent_payload_sql'];
+$independent_payload_sql_order = $_POST['independent_payload_sql_order'];
+require_once __DIR__ . "/templater_common.php";
+?>
+
+<script>
+<?php minStart(); ?>
+
+function sendOne(skip) {
+ if (typeof skip !== 'boolean')
+ skip = false;
+
+ if (typeof pivot.__skip !== 'undefined') {
+ if (pivot.__skip) {
+ delete pivot.__done;
+ delete pivot.__failed;
+ data[pivot.index].__skip = true;
+ redoRows($("tr[data-id='" + pivot.uid + "']")[0], 0, 0, 0, pivot.index);
+ redoRows($("tr[data-id='" + pivot.ID + "']")[0], 0, 0, 0, pivot.index);
+ redoRows($("tr[data-id='" + pivot.id + "']")[0], 0, 0, 0, pivot.index);
+ return true;
+ }
+ }
+
+ if (typeof pivot.__done !== 'undefined') {
+ if (!confirm(unescape('An diese Adresse wurde schon gesendet. Nochmal senden?')))
+ return;
+ }
+
+ var from = $('input[name=email_from]').val();
+ var to = $('input[name=email_to]').val();
+ var cc = $('input[name=email_cc]').val();
+ var bcc = $('input[name=email_bcc]').val();
+ var subject = $('input[name=email_subject]').val();
+ var html = $('#preview').html();
+
+ var persid = null;
+ var stipid = null;
+ var uid = null;
+
+ if (typeof pivot.ID !== 'undefined') persid = pivot.ID;
+ if (typeof pivot.id !== 'undefined') persid = pivot.id;
+ if (typeof pivot.PersID !== 'undefined') persid = pivot.PersID;
+ if (typeof pivot.persid !== 'undefined') persid = pivot.persid;
+ if (typeof pivot.StipID !== 'undefined') stipid = pivot.StipID;
+ if (typeof pivot.stipid !== 'undefined') stipid = pivot.stipid;
+ if (typeof pivot.UID !== 'undefined') uid = pivot.UID;
+ if (typeof pivot.uid !== 'undefined') uid = pivot.uid;
+
+ var noerror = false;
+ var response = "";
+ $.ajaxSetup({async:false});
+ $.ajax({
+ type: 'POST',
+ url: '/db/main/sendmail.php',
+ data: {
+ 'from': from,
+ 'to': to,
+ 'cc': cc,
+ 'bcc' : bcc,
+ 'html' : html,
+ 'subject': subject,
+ 'persid': persid,
+ 'stipid': stipid,
+ 'uid': uid,
+ 'attachment': $('#attachment :selected').val(),
+ 'attachment_fn': $('#attachment :selected').text()
+ },
+ success: function (d, s, x) {
+ if (d == 1) {
+ noerror = true;
+ response = x.responseText;
+ }
+ }
+ });
+
+ if (noerror && response == '1') {
+ pivot.__done = true;
+ delete pivot.__failed;
+
+ redoRows($("tr[data-id='" + pivot.uid + "']")[0], 0, 0, 0, pivot.index);
+ redoRows($("tr[data-id='" + pivot.ID + "']")[0], 0, 0, 0, pivot.index);
+ redoRows($("tr[data-id='" + pivot.id + "']")[0], 0, 0, 0, pivot.index);
+
+ if (!skip) alert('E-Mail an ' + to + ' erfolgreich gesendet.');
+
+ $('#email_status').html('[#' + (count_s + count_f + 1) + '] E-Mail erfolgreich: ' + to);
+ return true;
+ }
+
+ pivot.__failed = true;
+ delete pivot.__done;
+
+ redoRows($("tr[data-id='" + pivot.uid + "']")[0], 0, 0, 0, pivot.index);
+ redoRows($("tr[data-id='" + pivot.ID + "']")[0], 0, 0, 0, pivot.index);
+ redoRows($("tr[data-id='" + pivot.id + "']")[0], 0, 0, 0, pivot.index);
+
+ alert('Fehler beim Senden an ' + to + ': ' + response);
+ $('#email_status').html('Fehler: ' + to);
+ return false;
+}
+
+function waitASec(time) {
+ $.ajaxSetup({async:false});
+ $.post('/db/main/delay.php', {
+ time: time
+ });
+}
+
+function sendAllDone() {
+ $('#email_modal').hide();
+ window.scrollTo(0, 0);
+ alert(unescape('Fertig. Erfolg für ' + count_s + ' Empfänger.\nFehlgeschlagen bei ' + count_f + '.'));
+ $('#email_status').html('Erfolg: ' + count_s + ', Fehler: ' + count_f);
+}
+
+function sendAllLoop() {
+ if (typeof window.emergencyOff !== 'undefined') {
+ sendAllDone();
+ delete window.emergencyOff;
+ return;
+ }
+
+ if (typeof pivot.__done === 'undefined') {
+ $('#email_status').html('Sende an: ' + pivot['email']);
+
+ if (sendOne(true)) count_s++;
+ else count_f++;
+
+ pivot_iter++;
+ if (pivot_iter < data.length) {
+ pivot = data[pivot_iter];
+ bP();
+ setTimeout(sendAllLoop, 100);
+ return;
+ }
+
+ setTimeout(sendAllDone, 100);
+ return;
+ }
+
+ pivot_iter++;
+ if (pivot_iter < data.length) {
+ pivot = data[pivot_iter];
+ bP();
+ setTimeout(sendAllLoop, 10);
+ return;
+ }
+
+ setTimeout(sendAllDone, 100);
+ return;
+}
+
+function sendAll() {
+ if (!confirm(unescape('Wirklich E-Mail an ' + $.grep(data, function (e) { return (typeof e.__done === 'undefined'); }).length + ' Empfänger senden?')))
+ return;
+
+ count_s = 0;
+ count_f = 0;
+ pivot = data[0];
+ pivot_iter = 0;
+ bP();
+
+ window.scrollTo(0, 0);
+ $('#email_modal').show();
+ setTimeout(sendAllLoop, 100);
+}
+
+function abortAll() {
+ window.emergencyOff = true;
+}
+
+<?php minEnd(); ?>
+</script>
+
+<?
+include_once __DIR__ . "/footer.php";
+?>
diff --git a/form.scm b/form.scm
new file mode 100644
index 0000000..628c6cf
--- /dev/null
+++ b/form.scm
@@ -0,0 +1,989 @@
+; vim: set et :
+(use-modules (ice-9 regex))
+(use-modules (srfi srfi-27))
+(load "lang-tags.scm")
+
+(define haddelitembtnjs #f)
+(define haddelcmitembtnjs #f)
+
+(define (current-year)
+ (string->number (strftime "%Y" (localtime (current-time)))))
+
+(define (random-string len)
+ (define (iter n s)
+ (if (< n 1)
+ s
+ (iter (- n 1) (string-append s (string (integer->char (+ (random-integer 24) 65)))))))
+ (iter len ""))
+
+(define (decide-by-lang de en)
+ `((!? "if (strstr($_SERVER['REQUEST_URI'], '/application/') || strstr($_SERVER['REQUEST_URI'], '/review/') || basename(dirname($_SERVER['REQUEST_URI'])) === 'en') {")
+ ,en
+ (!? "} else {")
+ ,de
+ (!? "}")))
+
+(define* (stepform num h1 h2 lis #:key (last #f) (elements-before-overlay `'()) (php-args "") (appname "ds") )
+ `((!span "") ; dummy for tidy
+ (!php ,(string-append"/* vim: set ts=4 sw=4 et : */ if (!isset($doPDF)) {" php-args))
+ (@p (define stepform_lis ',lis))
+ (@p (main ,h1 ,h2
+ (append
+ ,elements-before-overlay
+ `((!? "}")
+ (@p (overlay
+ `(@p (fieldset
+ (string-append ,,,appname (number->string ,,,num) "App")
+ (string-append "app" (number->string ,,,num) "Ctrl")
+ stepform_lis))))
+ (!? "if (!isset($doPDF)) {")
+ (!p "* " (@p (parse-mess "?mandatory")))
+ (!div #((id uibottom)(style "display: none;"))
+ ,(if (= ,num 2)
+ `((!div #((style "display: inline-block; width: 49%;"))
+ (!button #((style "background: darkgrey; margin-right: .5em;")
+ (onclick "return saves2();"))
+ ,(parse-mess "?save")))
+ (!div #((style "display: inline-block; width: 49%; text-align: right;"))
+ (!button #(onclick "return continue_step3();")
+ ,(parse-mess "?next")))))
+ ,(if (and (> ,num 2) (not ,last))
+ `((!div #((style "display: inline-block; width: 49%;"))
+ (!button #((style "background: darkgrey;")
+ (onclick "return save();"))
+ ,(parse-mess "?save")))
+ (!div #((style "display: inline-block; width: 49%; text-align: right;"))
+ (!button #((onclick ,(string-append "return return_step" (convert (- ,num 1)) "();"))
+ (style "margin-right: .5em;"))
+ ,(parse-mess "?back"))
+ (!button #(onclick ,(string-append "return continue_step" (convert (+ ,num 1)) "();"))
+ ,(parse-mess "?next")))))
+ ,(if ,last
+ `((!div #((style "display: inline-block; width: 49%;"))
+ (!button #((style "background: darkgrey;")
+ (onclick "return save();"))
+ ,(parse-mess "?save")))
+ (!div #((style "display: inline-block; width: 49%; text-align: right;"))
+ (!button #((onclick ,(string-append "return return_step" (convert (- ,num 1)) "();"))
+ (style "margin-right: .5em;"))
+ ,(parse-mess "?back"))
+ (!button #(onclick "return check_and_send();")
+ ,(parse-mess "?send"))))))
+ (!? "}")))))
+ (@p (minjs
+ `((@p (stepdatajs ,,num ,,appname))
+ (@p (stepsubmitjs ,,num ,,appname))
+ (@p (stepbootstrapjs ,,num ,,appname))
+ (@dump ,(string-append "s" (number->string ,num) ".js"))
+ (@p (stepjs ,,num ,,appname))
+ )))))
+
+(define (stepdatajs num appname)
+ (let ((n (number->string num)))
+ `(,(string-append
+ "window.s" n " = {};\n"
+ "try { window.s" n " = JSON.parse('<?php echo str_replace(\"'\", \"\\'\", str_replace(\"\\\\\", \"\\\\\\\\\", $s" n ")); ?>');\n"
+ "} catch (e) { ; };\n"))))
+
+(define (stepsubmitjs num appname)
+ (let ((n (number->string num)))
+ `(,(string-append
+ "function submitValues() {\n"
+ "return safeSubmit({\n"
+ "'s': " n ",\n"
+ "'uid': '<?php echo $_SESSION['uid']; ?>',\n"
+ "'value': acc()\n"
+ "});\n"
+ "}\n"))))
+
+(define (stepbootstrapjs num appname)
+ (let ((n (number->string num)))
+ `(,(string-append
+ "function stepBootstrap" (convert num) "() {\n"
+ "window.app" n " = angular.module('" appname n "App', []);\n"
+ "window.app" n ".controller('app" n "Ctrl', ['$scope', function ($scope) {\n"
+ "$scope.d = window.s" n ";\n"
+ "}]);\n"
+ "angular.bootstrap($('#" appname n "App'), ['" appname n "App']);\n"
+ "}\n"
+ "function stepActivate" (convert num) "() {\n"
+ "$('#" appname n "App').show();\n"
+ "$('#" appname n "App input').trigger('change');\n"
+ "$('#" appname n "App select').trigger('change');\n"
+ "$('#uibottom').show();\n"
+ "}\n"
+ ))))
+
+(define (stepjs num appname)
+ (define func (random-string 10))
+ `((!? "if (isset($doPDF)) {")
+ ,(string-append "function " func "() { $('#" appname (convert num) "App input, #" appname (convert num) "App textarea, #" appname (convert num) "App select').prop('disabled', true); $('" appname (convert num) "App button').prop('disabled', true).css('display', 'none'); }")
+ ,(string-append "$(document).ready(function () {" func "(); let observer = new MutationObserver(function () { " func "(); }); observer.observe(document.querySelector('#" appname (convert num) "App'), {subtree: true, childList: true,}); });")
+ (!? "}")))
+
+(define* (main h1 h2 lis #:key (style "padding: 0;"))
+ `(!div #((class main)(style ,style))
+ (!h1 (@p (parse-mess ,h1)))
+ (!h2 (@p (parse-mess ,h2)))
+ ,lis))
+
+(define (minjs lis)
+ `(!script (!? "minStart();" ) "\n" ,lis "\n" (!? "minEnd();")))
+
+(define (newbuttonjs func variable)
+ `(,(minjs
+ (string-append
+ "function " func "(me) {\n"
+ " var v = JSON.parse(acc());\n"
+ " if (typeof v." variable " === 'object') v." variable ".push([]);\n"
+ " else v." variable " = [];\n"
+ " if (safeSubmit({\n"
+ " 's': $(me).closest('fieldset').attr('id').substr(2, 1),\n"
+ " 'uid': '<?php echo $_SESSION['uid']; ?>',\n"
+ " 'value': JSON.stringify(v)\n"
+ " })) location.reload();\n"
+ " return false;\n"
+ "}"))))
+
+(define (delitembuttonjs)
+ (unless #f
+ (minjs
+ `(,(set! haddelitembtnjs #t)
+ ,(string-append
+ "function delItem(o) {\n"
+ " o.parent().parent().remove();\n"
+ " if (submitValues()) location.reload();\n"
+ " return false;\n"
+ "}")))))
+
+(define (delcmitembuttonjs)
+ (unless haddelcmitembtnjs
+ (minjs
+ `(,(set! haddelitembtnjs #t)
+ ,(string-append
+ "function delCMItem(o) {\n"
+ " o.parent().remove();\n"
+ " if (submitValues()) location.reload();\n"
+ " return false;\n"
+ "}")))))
+
+;(define (textarea ...)
+(define (textarea text)
+ `(!textarea #((readonly "readonly")) ,text))
+
+;(define (textareaonside ...)
+(define (textareaonside width label text)
+ `((!div #(data-field-span ,width)
+ (!span ,label)
+ (!textarea #((readonly "readonly"))
+ ,text))))
+
+;(define (checkboxsimple ...)
+(define (checkboxsimple width name label)
+ `((!div #(data-field-span ,width)
+ (!input #((type "checkbox")
+ (name ,name)
+ (ng-checked ,(string-append "d." name)))
+ "&nbsp;" ,(parse-mess label)))))
+
+;(define (checkboxconglomerate (mehrere mit <br> getrennt, siehe ds/s5.php ...)
+(define (checkboxconglomerate width checkboxes textarea-label textarea-name)
+ (define (iter items)
+ (if (null? items)
+ '()
+ (let ((e (car items)))
+ (append
+ `((!input #((type "checkbox") (name ,(car e)) (ng-checked ,(string-append "d." (car e)))) ,(cadr e))
+ (!br))
+ (iter (cdr items))))))
+
+ `((!div #(data-row-span ,width)
+ (!div #(data-field-span ,width)
+ ,@(iter checkboxes)
+ (!br)
+ ,textarea-label ":"
+ (!textarea #((name ,textarea-name) (rows "2")) ,(string-append "{{ d." textarea-name " }}"))
+ (!br)))))
+
+
+;(define (multicontainar form classes variable labeldelbtn labelnewbtn btnprefix lis)
+;(@p (multicontainer "3" "study" "studies" "Studium loeschen" "Weiteres Studium anlegen" "study" `((@p (legend "...") ....))))
+;(define (multicontainer width form classes variable labeldelbtn labelnewbtn btnprefix lis)
+; (if (null? lis) '() (let ((e (car items)))
+; (append (list ((
+
+; #:funcnewbtn - explicitly name btn
+; #:divargs - arguments for the outer div
+; #:labelduration - label text for the duration
+(define* (multicontainer width class name variable labeldelbtn labelnewbtn #:key (funcnewbtn (random-string 16)) (divargs #()) (labelduration "") (selectname "") (options '()) .opt)
+ `((!div ,(merge-html-args `#((class ,class) (ng-repeat ,(string-append "b in d." variable))) divargs)
+ ,(row width `(
+ (!div #((data-field-span 1) (class "mand")) ,(parse-mess name) "*")
+ ,(textareasimple 2 "" class #t #:prefix "b")
+ ,(if (string=? labelduration "") '() `("\n<? if (isset($doPDF)) { ?></div><div data-row-span=\"3\"><? } ?>"
+ (!div #((style "clear: both;")) "&nbsp;")
+ ;(!div #((data-field-span 1) (class "mand")) ,(parse-mess labelduration))
+ ;(!div #((data-field-span 2))
+ ,(selectonside 1 2 "" labelduration selectname options #t #:prefix "b" #:noempty #t)
+ (!div #((style "clear: both;")) "&nbsp;")
+
+ ))
+ (!button #((onclick "return delItem($(this));")) ,(parse-mess labeldelbtn))
+
+ )))
+ (!button #((id ,(string-append "btn_" class)) (onclick ,(string-append "return " funcnewbtn "(this);"))) ,(parse-mess labelnewbtn))
+ (@p (newbuttonjs ,funcnewbtn ,variable))
+ (@p (delitembuttonjs))
+ ))
+
+; #:funcnewbtn - explicitly name btn
+(define* (custommulticontainer class legend variable labeldelbtn labelnewbtn options #:key (funcnewbtn (random-string 16)))
+ `((!legend ,(parse-mess legend))
+ (!div ,(merge-html-args `#((style "margin-bottom: 1.5em;")(class ,class) (ng-repeat ,(string-append "b in d." variable))))
+ (,options ((!button #((onclick "return delCMItem($(this));")) ,(parse-mess labeldelbtn)))))
+ (!button #((id ,(string-append "btn_" class)) (onclick ,(string-append "return " funcnewbtn "(this);"))) ,(parse-mess labelnewbtn))
+ (@p (newbuttonjs ,funcnewbtn ,variable))
+ (@p (delcmitembuttonjs))
+ )
+)
+
+(define* (s5spacer #:key (includenbsp #f) . opt)
+ `(("<? if (isset($doPDF)) { ?></div><div data-row-span=\"3\"><? } ?>") (,(if includenbsp `(!div #((style "clear: both;")) "&nbsp;") '()))))
+
+
+(define* (social width1 width2 label inputs #:key (prefix "d") (message "") (textprefix "") (textarea #f) (comment_on '()) (aftermessage '()) (afterspan '()))
+ `((!legend ,(parse-mess label))
+ ,(row width1
+ `((!div
+ #((data-field-span ,width2))
+ ,@(let iter ((i inputs))
+ (if (null? i)
+ '()
+ (append
+ `((!input ,`#((type "checkbox") (name ,(car i)) (ng-checked ,(string-append prefix "." (car i)))))
+ "&nbsp;" ,(parse-mess (string-append textprefix (car i)))
+ (!div #((style "clear:both; margin-bottom: .4em;")))
+ ,(if (member (car i) comment_on)
+ `((!div #((style "display: none; margin-top: .3em; margin-left: 2em; padding-bottom: .8em;")
+ (id ,(string-append (car i) "_comment")))
+ ,(parse-mess "?social_comment") "*"
+ (!textarea #((class "mand")
+ (ng-value ,(string-append prefix "." (car i) "_comment"))
+ (name ,(string-append (car i) "_comment")))))
+ (!script
+ ,(string-append
+ "$('input[name=" (car i) "]').on('change', function () {"
+ " if ($(this).is(':checked')) {"
+ " $('#" (car i) "_comment').show();"
+ " } else {"
+ " $('#" (car i) "_comment').hide();"
+ " }"
+ "});")))))
+ (iter (cdr i)))))
+ ,(if textarea
+ `((!br)
+ ,(parse-mess-as-text (string-append textprefix "sonstiges"))
+ (!textarea #((name "sonstiges") (rows "2")) ,(string-append "{{ " prefix ".sonstiges }}")))
+ `((!br)
+ ,(string-append (parse-mess-as-text (string-append textprefix "sonstiges")) "&nbsp;")
+ (!textarea #((name "sonstiges")) ,(string-append "{{ " prefix ".sonstiges }}"))))
+ (!br)
+ ,(if (string=? message "") '() `(!p (!i #((class "fas fa-exclamation-triangle"))) " " ,(parse-mess message)))
+ ,aftermessage)
+ ,afterspan))))
+
+; fieldsetargs - arguments for the fieldset element
+(define* (fieldset app cntrl lis #:key (fieldsetargs #()) .opt)
+ `(!fieldset ,(merge-html-args `#((id ,app) (ng-controller ,cntrl) (style "display: none;")) fieldsetargs)
+ ,@lis))
+
+; #:formargs - arguments for the grid-form
+(define* (grid-form lis #:key (formargs #()))
+ `(!form ,(merge-html-args `#((class "grid-form")) formargs) ,@lis)
+)
+
+; #:divargs - arguments for the overlay div
+; #:formargs - arguments for the grid-form
+(define* (overlay lis #:key (divargs #()) (formargs #()) . opt)
+ `(!div ,(merge-html-args `#((id "overlay")) divargs) (!form ,(merge-html-args `#((class "grid-form")) formargs) ,@lis)))
+
+;textsimple pruefen, sollte ds/s3.php "Geben Sie Ihre aktuelle ...."
+
+;(define (row width lis . style)
+; `(!div #(data-row-span ,width)
+; ,lis))
+
+; #:divargs - arguments of the div
+(define* (row width lis #:key (divargs #()) . opt)
+ `(!div ,(merge-html-args `#((data-row-span ,width)) divargs) ,lis))
+
+
+; (if (= (length class) 2)
+
+;(define (row width lis . style)
+; `(!div #(data-row-span ,width)
+; ,lis))
+
+(define (tabular width1 width2 col1name col2name sumfield1 sumfield2 l)
+ (define (tabular-inner width1 width2 l)
+ (let iter ((lis l))
+ (if (null? lis)
+ '()
+ (cons `(@p (row ,(+ width1 width2)
+ `((!div #(data-field-span ,,width1) ,,(caar lis))
+ (!div #(data-field-span ,,width2) ,,(cadar lis)))))
+ (iter (cdr lis))))))
+
+ (append `((@p (row ,(+ width1 width2)
+ `((!div #(data-field-span ,,width1) ,,col1name)
+ (!div #(data-field-span ,,width2) ,,col2name)))))
+
+ (tabular-inner width1 width2 l)
+
+ `((@p (row ,(+ width1 width2)
+ `((!div #(data-field-span ,,width1) ,,sumfield1)
+ (!div #(data-field-span ,,width2) ,,sumfield2)))))
+
+ ))
+
+;(define (tabular2 width1 width2 col1name col2name sumfield1 sumfield2 lis1 lis2)
+; (let iter ((lis1 lis1) (lis2 lis2))
+; (if (or (null? lis1) (null? lis2))
+; '()
+; (let ((e1 (car lis1))
+; (e2 (car lis2)))
+; (cons `(@p (row ,(+ width1 width2)
+; `((!div #(data-field-span ,width1) ,e1)
+; (!div #(data-field-span ,width2) ,e2))))
+; (iter (cdr lis1) (cdr lis2))))))
+;
+; (append `((@p (row ,(+ width1 width2))
+; `((!div #(data-field-span ,width1) ,col1name)
+; (!div #(data-field-span ,width2) ,col2name))))
+; (iter lis1 lis2)
+; `((@p (row ,(+ width1 width2))
+; `((!div #(data-field-span ,width1) ,sumfield1)
+; (!div #(data-field-span ,width2) ,sumfield2))))))
+
+
+(define (tip1 name . tip)
+ (if (null? tip) '() `(!span #(("style" "font-size: 1.2rem; text-transform: none; cursor: pointer;")
+ ("onmouseover" ,(string-append "$('#" name "_tip').show();"))
+ ("onmouseleave" ,(string-append "$('#" name "_tip').hide();"))
+ ("onclick" ,(string-append "$('#" name "_tip').toggle();")))
+ "ⓘ")))
+
+(define (tip2 name . tip)
+ (if (null? tip) '() `(!p #(("id" ,(string-append name "_tip"))
+ ("style" "display: none; font-size: 1rem; text-align: justify; hyphens: auto;"))
+ ,tip)))
+
+;upload form used in s6
+; #:dataformat - a list of 2 elements specifying the type and accepted documents in input
+; #:info - optional list of elements which will be placed below the upload fields
+; #:inputclasses - html classes to be added to the file input field
+; #:titlemandargs - a string, mainly used to add a " * " in the title infront of the " : ". The mand class would put it after the " : "
+(define* (s6-upload-form doctype #:key (dataformat '("pdf" "application/pdf, .pdf")) (info `()) (inputclasses "mand") (titlemandargs "*") .options)
+ `(!div #(class "upload")
+ (!b #() ,(string-append (parse-mess-as-text (string-append "?s6_" doctype)) titlemandargs))
+ (!php ,(string-append "$e = false; if (file_exists(\"../../uploads/\" . $puid . \"_\" . \"" doctype "." (car dataformat) "\")) { $e = true;"))
+ (!span " " (!a #((class "uploaded") (href ,(string-append "download.php?" doctype)) (target "_blank")) ,(parse-mess "?hochgeladen")))
+ (!form #((style "display: inline-block;") (action "delupload.php") (method "post") (enctype "multipart/form-data"))
+ (!input #((type "hidden") (name ,doctype) (value "1")))
+ (!input #((type "submit") (class "delete") (onclick "return submitValues();") (value ,(parse-mess-as-text "?del"))))
+ )
+ (!input #((type "hidden") (name ,(string-append doctype "_done")) (class "done") (value "1")))
+ (!input #((type "hidden") (name "done") (class "done") (value "1")))
+ (!php "}")
+ (!form #((action "upload.php") (method "post") (enctype "multipart/form-data"))
+ (!input #((type "hidden") (name ,doctype) (value "1")))
+ (!input #((type "file") (class ,inputclasses) (name "file") (accept ,(cadr dataformat))))
+ (!input #((type "submit") (style "display: none;") (onclick "return submitValues();") (value "<?php if ($e) { echo $mess['replace']; } else { echo $mess['upload']; } ?>") (name "upload")))
+ )
+ ,info
+ )
+)
+
+; a small infoparagraph
+; #:class - class to be added to the <i>
+; #:style - style of the <p>
+(define* (info-paragraph text #:key (class "fa fa-info") (style "padding-left: 2em; margin-top: .5em; margin-bottom: .5em; font-size: 80%;") (want-space #t) . opt)
+ `(!p ,(if (string=? style "") `#() `#((style ,style)))
+ ,(if want-space `((!i #((class ,class))) " ") `())
+ ,(parse-mess text)
+ ))
+
+; a list containing an enumumeration of texts
+; #:etradivstyleargs - name says it all (extra style arguments for the outer div)
+; #:extralistitemargs - a list of extra style arguments for the enumerated texts
+; -> if there are 7 items and only item 5 should have an extra style argument the list needs to be '("" "" "" "" "style")
+; #:optionalelements - a list of optional elements to be added after the enumeration
+; #:upperulmargintop - the margin for the <ul>
+(define* (info-list title list-items #:key (extradivstyleargs "") (extralistitemargs '()) (optionalelements '()) (upperulmargintop ".2em"). opt)
+ `(!div #((style ,(string-append extradivstyleargs " padding-left: 2em; padding-top: .7em; font-size: 80%;")))
+ (!i #((class "fa fa-info"))) " "
+ ,(parse-mess title)
+ (!ul #((style ,(string-append "margin-top: " upperulmargintop "; padding-left: 1.8em;")))
+ ,@(let iter ((item list-items) (arg extralistitemargs))
+ (if (null? item)
+ '()
+ (append `(
+ (!li ,(if (string-null? (getstringhelper item arg)) `#() `#((style ,(getstringhelper item arg)))) ,(parse-mess (car item)))
+ ,(iter (cdr item) (if (null? arg) '() (cdr arg))))))
+ ))
+ ,@(let iter2 ((option optionalelements))
+ (if (null? option)
+ '()
+ (append
+ `(,(car option))
+ (iter2 (cdr option))))
+ )
+ ))
+
+; a helper method for info-list (see above)
+(define (getstringhelper item arg)
+ (string-append (if (or (null? arg) (string-null? (car arg))) "" (car arg)) (if (null? (cdr item)) "" "padding-bottom: .3em;")))
+
+; #:tip - the tooltip text
+; #:tipname - name for the tooltip
+; #:divargs - arguments for the outer div
+; #:labelargs - arguments for the label
+; #:selectargs - arguments for the select element
+; #:optionargs - arguments for each option in the select (only if not php)
+; #:noempty - removes the empty option from the select
+(define* (selectsimple width classes label name options mand #:key (usesprefix #f) (tip "") (tipname "") (divargs #()) (labelargs #()) (selectargs #()) (optionargs #()) (noempty #f) (prefix "d") (afterselect `()) . opt)
+ `(!div ,(merge-vectors
+ `#((data-field-span ,width))
+ (if (string=? classes "")
+ `#()
+ `#((class ,classes))
+ )
+ divargs)
+ (!label ,(merge-html-args (if (not mand) #() `#((class "mand"))) labelargs)
+ (,(parse-mess label) ,(if (string=? tip "") '() (tip1 (if (string=? tipname "") name tipname) (parse-mess tip)))))
+ (!select ,(merge-vectors
+ `#((name ,name))
+ (if (string=? (string-append (if (not mand) "" "mand ") classes) "")
+ `#()
+ `#((class ,(string-append (if (not mand) "" "mand ") classes)))
+ )
+ selectargs)
+ ,(if noempty '() `(!option ,(merge-html-args `#((value "")) optionargs) ""))
+ ,@(let iter ((opts options))
+ (if (null? opts)
+ '()
+ (if (string=? (caar opts) "!php")
+ (append `(!php ,(cadar opts))
+ (iter (cdr opts)))
+ (append `((!option ,(merge-html-args `#((value ,(caar opts))
+ (ng-selected ,(string-append (if usesprefix "s." (string-append prefix ".")) name " == '" (caar opts) "'"))) optionargs)
+ ,(parse-mess (cadar opts))))
+ (iter (cdr opts)))))))
+ ,(if (string=? tip "") '() (tip2 (if (string=? tipname "") name tipname) (parse-mess tip)))
+ ,afterselect))
+
+; #:tip - the tooltip message
+; #:div1args - arguments for the first div
+; #:labelargs - arguments for the label in the first div
+; #:div2args - arguments for the second div
+; #:inputargs - arguments for the input in the second div
+(define* (inputonside width1 width2 classes label name mand #:key (tip "") (div1args #()) (labelargs #()) (div2args #()) (inputargs #()) (prefix "d") . opt)
+ `((!div ,(merge-html-args `#((data-field-span ,width1)) div1args)
+ (!span ,(merge-html-args (if (not mand) #() `#((class "mand"))) labelargs)
+ (,(parse-mess label) ,(if (string=? tip "") '() (tip1 name (parse-mess tip))))))
+ (!div ,(merge-html-args `#((data-field-span ,width2)) div2args)
+ (!input ,(merge-vectors
+ (if (string=? (string-append (if (not mand) "" "mand ") classes) "")
+ `#()
+ `#((class ,(string-append (if (not mand) "" "mand ") classes))))
+ `#((name ,name))
+ `#((type "text"))
+ `#((value ,(string-append "{{ " prefix "." name " }}")))
+ inputargs))
+ ,(if (string=? tip "") '() (tip2 name (parse-mess tip))))))
+
+; #:tip - the tooltip message
+; #:div1args - arguments for the first div
+; #:labelargs - arguments for the label in the first div
+; #:div2args - arguments for the second div
+; #:textareaargs - arguments for the textarea in the second div
+(define* (textareaonside width1 width2 classes label name mand #:key (tip "") (div1args #()) (labelargs #()) (div2args #()) (textareaargs #()) (prefix "d") . opt)
+ `((!div ,(merge-html-args `#((data-field-span ,width1)) div1args)
+ (!span ,(merge-html-args (if (not mand) #() `#((class "mand"))) labelargs)
+ (,(parse-mess label) ,(if (string=? tip "") '() (tip1 name (parse-mess tip))))))
+ (!div ,(merge-html-args `#((data-field-span ,width2)) div2args)
+ (!textarea ,(merge-vectors
+ (if (string=? (string-append (if (not mand) "" "mand ") classes) "")
+ `#()
+ `#((class ,(string-append (if (not mand) "" "mand ") classes))))
+ `#((name ,name))
+ textareaargs) ,(string-append "{{ " prefix "." name " }}"))
+ ,(if (string=? tip "") '() (tip2 name (parse-mess tip))))))
+
+
+
+(define (parse-mess text)
+ (if (and (> (string-length text) 0) (string=? (substring text 0 1) "?"))
+ `(!= ,(string-append "$mess['" (substring text 1 (string-length text)) "']"))
+ `(,text)))
+
+(define (parse-mess-as-text text)
+ (if (and (> (string-length text) 0) (string=? (substring text 0 1) "?"))
+ (string-append "<?php echo $mess['" (substring text 1 (string-length text)) "']; ?>")
+ text))
+
+; #:legendargs - arguments for the legend
+(define* (legend text #:key (legendargs #()) . opt)
+ `(!legend ,legendargs ,(parse-mess text)))
+
+; #:tip - the tooltip message
+; #:div1args - arguments for the outer div
+; #:labelargs - arguments for the label
+; #:div2args - arguments for the inner div
+; #:selectargs - arguments for the select element
+; #:optionargs - arguments for each option of the select
+; #:prefix -
+; #:noempty - remove the empty option from the select
+(define* (selectonside width1 width2 classes label name options mand #:key (tip "") (div1args #()) (labelargs #()) (div2args #()) (selectargs #()) (optionargs #()) (prefix "d") (noempty #f) . opt)
+ `((!div ,(merge-html-args `#((data-field-span ,width1)) div1args)
+ (!span ,(merge-html-args (if (not mand) '() #((class "mand"))) labelargs)
+ (,(parse-mess label)
+ ,(if (string=? tip "") '() `(!span #(("style" "font-size: 1.2rem; text-transform: none; cursor: pointer;")
+ ("onmouseover" ,(string-append "$('#" name "_tip').show();"))
+ ("onmouseleave" ,(string-append "$('#" name "_tip').hide();"))
+ ("onclick" ,(string-append "$('#" name "_tip').toggle();")))
+ "ⓘ")))))
+ (!div ,(merge-html-args `#((data-field-span ,width2)) div2args)
+ (!select ,(merge-html-args `#((class ,(string-append (if (not mand) "" "mand ") classes))
+ (name ,name)) selectargs)
+ ,(if noempty '() `(!option ,(merge-html-args `#((value "")) optionargs) ""))
+ ,@(let iter ((opts options))
+ (if (null? opts)
+ '()
+ (if (string=? (caar opts) "!php")
+ (append `((!php ,(cadar opts)))
+ (iter (cdr opts)))
+ (append `((!option ,(merge-html-args `#((value ,(caar opts))
+ (ng-selected ,(string-append prefix "." name " == '" (caar opts) "'"))) optionargs)
+ ,(parse-mess (cadar opts))))
+ (iter (cdr opts)))))))
+ ,(if (string=? tip "") '() `(!p #(("id" ,(string-append name "_tip"))
+ ("style" "display: none; font-size: 1rem; text-align: justify; hyphens: auto;"))
+ ,(parse-mess tip))))))
+
+; #:div1args - arguments for the first div
+; #:labelargs - arguments for the label in the first div
+; #:div2args - arguments for the second div
+; #:spanargs - arguments for the span in the second div
+; #:tip - the tex of the tooltip
+(define* (textsimple width classes label text mand #:key (div1args #()) (labelargs #()) (div2args #()) (spanargs #()) (tip "") . opt)
+ `((!div ,(merge-html-args `#((data-field-span ,width)) div1args)
+ (!label ,(merge-html-args (if (not mand) '() `#((class "mand"))) labelargs) ,(parse-mess label))
+ ,(if (string=? tip "") '() (tip1 label tip)))
+ (!div ,(merge-html-args `#((data-field-span ,width)) div2args)
+ (!span ,(merge-html-args `#((class ,(string-append (if (not mand) "" "mand ") classes))) spanargs)
+ ,(parse-mess text))
+ ,(if (string=? tip "") '() (tip2 label tip)))))
+
+
+; #:divargs - arguments for the outer div of the calculationresult
+; #:labelargs - arguments for the label
+; #:inputargs - arguments for the input field
+; #:readonly - if the inputfield should be read-only
+; #:maxlength - the maxlength argument value
+; #:size - the site argument value
+(define* (calculationinput width classes label name mand #:key (divargs #()) (labelargs #()) (inputargs #()) (readonly #f) (maxlength -1) (size -1) . opt)
+ `(!div ,(merge-html-args `#((data-field-span ,width)(style "background: lightgrey;")) divargs)
+ (!label ,(merge-html-args `#((for ,name)) labelargs) ,(parse-mess label))
+ (!input ,(merge-vectors `#((class ,(string-append (if mand "mand" "") classes))
+ (type "text")
+ (name ,name)
+ (value ""))
+ (if readonly `#((readonly "readonly")) #())
+ (if (not (= maxlength -1)) `#((maxlength ,maxlength)) #())
+ (if (not (= size -1)) `#((size ,size)) #())
+ inputargs))))
+
+; #:divargs - arguments for the outer div of the button
+; #:buttonargs - arguments for the button itself
+(define* (simplebutton width onclick label #:key (divargs #()) (buttonargs #()) . opt)
+ `(!div ,(merge-html-args `#((data-field-span ,width)) divargs)
+ (!button ,(merge-html-args `#((onclick ,onclick)) buttonargs) ,(parse-mess label))))
+
+;; Simple definition of fold-left (left fold)
+(define (fold-left f acc lst)
+ (if (null? lst)
+ acc
+ (fold-left f (f acc (car lst)) (cdr lst))))
+
+
+(define (merge-vectors . vectors)
+ (define (merge-alists a1 a2)
+ (let ((filtered-a1 (filter (lambda (pair) (not (assoc (car pair) a2))) a1)))
+ (append a2 filtered-a1)))
+
+ (define alists (map vector->list vectors))
+
+ (define merged-alist
+ (fold-left merge-alists '() alists))
+
+ (list->vector merged-alist))
+
+
+(define (merge-html-args old . new)
+ (merge-vectors old (if (null? new) #() (car new))))
+
+(define (fieldsimple width lis)
+ `(!div #(data-field-span ,width)
+ ,lis))
+
+; #:divargs - arguments for the outer div of the input
+; #:tip - tooltip text
+; #:usesprefix - if the input should use d.name or s.name, #t for s.name
+; #:labelargs - arguments for the label
+; #:inputargs - arguments for the input
+; #:tipname - use this instead of the name parameter for the name of the tip
+; #:warning - add a warning after the label-text in the label
+; #:readonly - readonly/protected
+; #:type - the type of the input field (default "text")
+(define* (inputsimple width classes label name mand #:key (divargs #()) (tip "") (usesprefix #f) (labelargs #()) (inputargs #()) (tipname "") (warning "") (ng-value #t) (readonly #f) (type "text") (value ""))
+ `((!div ,(merge-html-args
+ `#((data-field-span ,width))
+ divargs)
+
+ (!label ,(merge-html-args
+ (if (not mand) #() `#((class "mand")))
+ labelargs)
+ ,(string-append
+ (parse-mess-as-text label)
+ (if (string=? warning "")
+ ""
+ (string-append "<span 'color: red;'>" (parse-mess warning) "</span>")))
+ ,(if (string=? tip "") '() (tip1 (if (string=? tipname "") name tipname) (parse-mess tip))))
+
+ (!input ,(merge-vectors
+ (if (string=? (string-append (if (not mand) "" "mand ") classes) "")
+ `#((name ,name) (type "text"))
+ `#((class ,(string-append (if (not mand) "" "mand ") classes)) (name ,name) (type ,type))
+ )
+ (if ng-value
+ `#((value ,(string-append (if usesprefix "{{ s." "{{ d.") (if (string=? value "") name value) " }}")))
+ `#()
+ )
+ (if readonly
+ `#((protected protected)(readonly readonly))
+ `#())
+ inputargs))
+
+ ,(if (string=? tip "") '() (tip2 (if (string=? tipname "") name tipname) (parse-mess tip))))))
+
+; OBSOLETE
+(define* (inputwithwarning width classes label warning name mand #:key (divargs #()) (tip "") (usesprefix #f) (labelargs #()) (inputargs #()) (tipname "") . opt)
+ `(,(inputsimple width classes label name mand #:warning warning)))
+
+; OBSOLETE
+(define (inputwithextras width classes style label name tipname mand . opt)
+ `(,(inputsimple width classes label name mand #:tipname tipname)))
+
+
+(define (centeredtextinfo style1 title text)
+ `((!center #() (!b #() ,(parse-mess title)))
+ (!p #(style ,style1) ,(parse-mess text))
+ ))
+
+; items - '(#(value labeltext) #(value labeltext))
+(define* (simpleradiobuttons name items #:key (backgroundcolor "#eee") (mand #f))
+ `(!div #(style ,(string-append "background: " backgroundcolor "; border: 1px solid lightgrey; padding: .3em;"))
+ ,@(let iter ((item items))
+ (if (null? item)
+ '()
+ (append
+ `((!div #()
+ (!p #()
+ (!input
+ ,(if mand
+ `#((type "radio") (id ,(if (<= (vector-length (car item)) 2) name (vector-ref (car item) 2))) (class "mand") (name ,name) (value ,(vector-ref (car item) 0)))
+ `#((type "radio") (id ,(if (<= (vector-length (car item)) 2) name (vector-ref (car item) 2))) (name ,name) (value ,(vector-ref (car item) 0)))))
+ (!label
+ #((for ,(vector-ref (car item) 0))) ,(parse-mess (vector-ref (car item) 1))))))
+ (iter (cdr item))
+ ))
+ )
+ ))
+
+; texts - a list
+(define* (legalstuffinfo title texts #:key (optionalelements `()))
+ `((!h3 #() ,(parse-mess title))
+ ,@(let iter ((text texts))
+ (if (null? text)
+ '()
+ (append
+ `(,(info-paragraph (car text) #:class "" #:style "" #:want-space #f))
+ (iter (cdr text)))))
+ ,optionalelements))
+
+(define (checkboxforlegalstuff name text)
+ `(!p #()
+ (!input #((class "mand") (type "checkbox") (name ,name) (id ,name)))
+ (!b #() ,(parse-mess text))))
+
+(define (small width label)
+ `(!div #(data-field-span "1")
+ (!small #() ,(parse-mess label))))
+
+
+; #:divargs - arguments for the outer div of the textarea
+; #:tip - tooltip text
+; #:usesprefix - if the input should use d.name or s.name, #t for s.name
+; #:labelargs - arguments for the label
+; #:textareaargs - arguments for the textarea
+; #:tipname - use this instead of the name parameter for the name of the tip
+; #:warning - add a warning after the label-text in the label
+; #:label - if the textare should have an aditional label
+(define* (textareasimple width classes name mand #:key (divargs #()) (tip "") (labelargs #()) (textareaargs #()) (tipname "") (warning "") (nolabel #f) (prefix "s") (label "") . opt)
+ `((!div ,(merge-html-args
+ `#((data-field-span ,width))
+ divargs)
+
+ ,(if (string=? label "") '() `(!label ,(merge-html-args
+ (if (not mand) #() `#((class "mand")))
+ labelargs)
+ ,(string-append
+ (parse-mess-as-text label)
+ (if (string=? warning "")
+ ""
+ (string-append "<span 'color: red;'>" (parse-mess warning) "</span>")))
+ ,(if (string=? tip "") '() (tip1 (if (string=? tipname "") name tipname) (parse-mess tip)))))
+
+ (!textarea ,(merge-html-args
+ `#((class ,(string-append (if (not mand) "" "mand ") classes))
+ (name ,name))
+ textareaargs) ,(string-append "{{ " prefix "." name " }}")))
+
+ ,(if (string=? tip "") '() (tip2 (if (string=? tipname "") name tipname) (parse-mess tip)))))
+
+(define simple-from-until-as-month-and-year-descending
+ `(,(selectsimple 1 "" "?von_monat" "startmonat"
+ (let iter ((mth 1))
+ (if (<= mth 12)
+ (cons `(,(convert mth) ,(convert mth)) (iter (+ mth 1)))
+ '()))
+ #t #:prefix "b")
+ ,(selectsimple 1 "" "?von_jahr" "startjahr"
+ (let iter ((yr (current-year)))
+ (if (>= yr 1970)
+ (cons `(,(convert yr) ,(convert yr)) (iter (- yr 1)))
+ '()))
+ #t #:prefix "b")
+ ,(selectsimple 1 "" "?bis_monat" "endemonat"
+ (let iter ((mth 1))
+ (if (<= mth 12)
+ (cons `(,(convert mth) ,(convert mth)) (iter (+ mth 1)))
+ '()))
+ #t #:prefix "b")
+ ,(selectsimple 1 "" "?bis_jahr" "endejahr"
+ (let iter ((yr (current-year)))
+ (if (>= yr 1970)
+ (cons `(,(convert yr) ,(convert yr)) (iter (- yr 1)))
+ '()))
+ #t #:prefix "b")))
+
+(define simple-from-as-month-and-year-descending
+ `(,(selectsimple 1 "" "?monat" "startmonat"
+ (let iter ((mth 1))
+ (if (<= mth 12)
+ (cons `(,(convert mth) ,(convert mth)) (iter (+ mth 1)))
+ '()))
+ #t #:prefix "b")
+ ,(selectsimple 1 "" "?jahr" "startjahr"
+ (let iter ((yr (current-year)))
+ (if (>= yr 1970)
+ (cons `(,(convert yr) ,(convert yr)) (iter (- yr 1)))
+ '()))
+ #t #:prefix "b")))
+
+
+(define* (moneytable name in ass out lia prefix #:key (ng-prefix "d") (in-class-outer "in") (in-class "sum_in")) `(
+ (!p #((style "color:black;"))
+ ,(parse-mess name))
+
+ ,(if in
+ `(
+ ,(row 3
+ `(
+ (!div #((data-field-span "2") (style "color: black;"))
+ (!b ,(parse-mess (string-append prefix "in"))))
+
+ (!div #((data-field-span "1") (style "color: black;"))
+ (!b ,(parse-mess (string-append prefix "betrag"))))
+ ))
+
+ (!div #(class ,in-class-outer) ,in)
+ ,(row 3
+ `(
+ (!div #((data-field-span "2"))
+ (!b #((style "color: black;")) ,(parse-mess (string-append prefix "summe"))))
+
+ (!div #((data-field-span "1"))
+ (!b #((style "color: black;") (class "sumfield") (id ,in-class))
+ ,(string-append "{{ " ng-prefix "." in-class " }}")))
+ )
+ #:divargs #((style "background: darkgrey;")))
+
+ (!br)
+ ))
+
+ ,(if ass
+ `(
+ ,(row 3
+ `(
+ (!div #((data-field-span "2") (style "color: black;"))
+ (!b ,(parse-mess (string-append prefix "ass"))))
+
+ (!div #((data-field-span "1") (style "color: black;"))
+ (!b ,(parse-mess (string-append prefix "betrag"))))
+ ))
+
+ (!div #(class ass) ,ass)
+
+ ,(row 3
+ `(
+ (!div #((data-field-span "2"))
+ (!b #((style "color: black;")) ,(parse-mess (string-append prefix "summe"))))
+
+ (!div #((data-field-span "1"))
+ (!b #((style "color: black;") (class "sumfield") (id "sum_ass"))
+ ,(string-append "{{ " ng-prefix ".sum_ass }}")))
+ ) #:divargs #((style "background: darkgrey;")))
+
+ (!br)
+ ))
+
+ ,(if out
+ `(
+ ,(row 3
+ `(
+ (!div #((data-field-span "2") (style "color: black;"))
+ (!b ,(parse-mess (string-append prefix "out"))))
+
+ (!div #((data-field-span "1") (style "color: black;"))
+ (!b ,(parse-mess (string-append prefix "betrag"))))
+ ))
+
+ (!div #(class out) ,out)
+
+ ,(row 3
+ `(
+ (!div #((data-field-span "2"))
+ (!b #((style "color: black;")) ,(parse-mess (string-append prefix "summe"))))
+
+ (!div #((data-field-span "1"))
+ (!b #((style "color: black;") (class "sumfield") (id "sum_out"))
+ ,(string-append "{{ " ng-prefix ".sum_out }}")))
+ )
+ #:divargs #((style "background: darkgrey;")))
+
+ ,(row 3
+ `(
+ (!div #((data-field-span "2"))
+ (!b #((style "color: black;")) ,(parse-mess (string-append prefix "semfee"))))
+
+ (!div #((data-field-span "1"))
+ (!input #((type "text") (class "value mand") (name "semfee") (value ,(string-append "{{ " ng-prefix ".semfee }}")))))
+ )
+ #:divargs #((style "background: #c1c1c1;")))
+
+ (!br)
+ ))
+
+ ,(if lia
+ `(
+ ,(row 3
+ `(
+ (!div #((data-field-span "2") (style "color: black;"))
+ (!b ,(parse-mess (string-append prefix "lia"))))
+
+ (!div #((data-field-span "1") (style "color: black;"))
+ (!b ,(parse-mess (string-append prefix "betrag"))))
+ ))
+
+ (!div #(class lia) ,lia)
+
+ ,(row 3
+ `(
+ (!div #((data-field-span "2"))
+ (!b #((style "color: black;")) ,(parse-mess (string-append prefix "summe"))))
+
+ (!div #((data-field-span "1"))
+ (!b #((style "color: black;") (class "sumfield") (id "sum_lia"))
+ ,(string-append "{{ " ng-prefix ".sum_lia }}")))
+ )
+ #:divargs #((style "background: darkgrey;")))
+ ))
+ ))
+
+(define* (moneytextinputrow class text input #:key (prefix "d")) `(
+ ,(row 3 `(
+ (!div #((data-field-span "2"))
+ (!b ,(parse-mess text))
+ )
+ (!div #((data-field-span "1"))
+ (!input #((name ,input) (class ,class) (value ,(string-append "{{ " prefix "." input " }}")) (type "text")))
+ )
+ ))
+))
+
+(define* (moneyinputinputrow class input1 input2 #:key (prefix "d") (placeholder1 "") (placeholder2 "")) `(
+ ,(row 3 `(
+ (!div #((data-field-span "2"))
+ (!input #((name ,input1) (value ,(string-append "{{ " prefix "." input1 " }}")) (type "text") (placeholder ,(parse-mess-as-text placeholder1))))
+
+ )
+ (!div #((data-field-span "1"))
+ (!input #((name ,input2) (class ,class) (value ,(string-append "{{ " prefix "." input2 " }}")) (type "text") (placeholder ,(parse-mess-as-text placeholder2))))
+ )
+ ))
+))
+
+(define* (kontoangabe rows) `(
+ ,@(let iter ((r rows))
+ (if (null? r)
+ '()
+ (append `(
+ ,(row (count-elements (car r)) `(
+ ,@(let iter ((e (car r)))
+ (if (null? e)
+ '()
+ (append `(
+ ,(inputsimple 1 "" (caar e) (cadar e) (caddr (cdar e)) #:value (caddar e) #:ng-value #t)
+ ) (iter (cdr e)))
+ )
+ )
+ ))
+ ) (iter (cdr r)))
+ )
+ )
+))
+
+(define (count-elements lst)
+ (if (null? lst)
+ 0
+ (+ 1 (count-elements (cdr lst)))))
+
+(define bundeslaender
+ `(("bw" "?land_bw")
+ ("by" "?land_by")
+ ("be" "?land_be")
+ ("bb" "?land_bb")
+ ("hb" "?land_hb")
+ ("hh" "?land_hh")
+ ("he" "?land_he")
+ ("mv" "?land_mv")
+ ("ni" "?land_ni")
+ ("nw" "?land_nw")
+ ("rp" "?land_rp")
+ ("sl" "?land_sl")
+ ("sn" "?land_sn")
+ ("st" "?land_st")
+ ("sh" "?land_sh")
+ ("th" "?land_th")))
diff --git a/futstips.php b/futstips.php
new file mode 100644
index 0000000..1991090
--- /dev/null
+++ b/futstips.php
@@ -0,0 +1,19 @@
+<?php
+/*
+ * 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.
+ */
+?>
+<?php
+/* vim: set ts=4 sw=4 et : */
+
+$_title = "Künftige Stipendien";
+$_constraint = "WHERE Stipendien.Förderende >= NOW() AND Stipendien.Förderbeginn > NOW() AND (ds.accepted=1 OR ds.accepted IS NULL OR ds.accepted = -1)";
+require_once __DIR__ . '/stips_common.php';
diff --git a/handle_css.php b/handle_css.php
new file mode 100644
index 0000000..f53892c
--- /dev/null
+++ b/handle_css.php
@@ -0,0 +1,18 @@
+<?php
+/* vim: set ts=4 sw=4 et : */
+
+if (strstr($_SERVER['REQUEST_URI'], 'handle_')) {
+ header("HTTP/1.0 404 Not Found");
+ exit(0);
+}
+
+require_once __DIR__ . '/common/vendor/autoload.php';
+
+use MatthiasMullie\Minify;
+$m = new Minify\CSS(
+ __DIR__ . '/common/css/' .
+ basename($_GET['css'])
+);
+
+header('Content-Type: text/css; charset=utf-8');
+echo $m->minify();
diff --git a/handle_js.php b/handle_js.php
new file mode 100644
index 0000000..bfddadf
--- /dev/null
+++ b/handle_js.php
@@ -0,0 +1,18 @@
+<?php
+/* vim: set ts=4 sw=4 et : */
+
+if (strstr($_SERVER['REQUEST_URI'], 'handle_')) {
+ header("HTTP/1.0 404 Not Found");
+ exit(0);
+}
+
+require_once __DIR__ . '/common/vendor/autoload.php';
+
+use MatthiasMullie\Minify;
+$m = new Minify\JS(
+ __DIR__ . '/common/js/' .
+ basename($_GET['js'])
+);
+
+header('Content-Type: application/javascript; charset=utf-8');
+echo $m->minify();
diff --git a/lang-tags.scm b/lang-tags.scm
new file mode 100644
index 0000000..2a9305e
--- /dev/null
+++ b/lang-tags.scm
@@ -0,0 +1,8 @@
+(define elements
+ (append elements
+ '((!php 3 "?php " " ?")
+ (!? 3 "?php " " ?")
+ (!= 3 "?php echo " "; ?")
+ (!twig 3 {{ }})
+ (!asp 3 "% " " %")
+ (!% 3 "% " " %"))))
diff --git a/orga.php b/orga.php
new file mode 100644
index 0000000..ef61680
--- /dev/null
+++ b/orga.php
@@ -0,0 +1,83 @@
+<?php
+/*
+ * 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.
+ */
+?>
+<?
+/* vim: set ts=4 sw=4 et : */
+
+require_once __DIR__ . "/check_auth.php";
+require_once __DIR__ . "/../includes/common.php";
+require_once __DIR__ . "/lookup.php";
+require_once __DIR__ . "/lookup_more.php";
+
+(function () use ($mysqli) {
+ global $orgaID, $superorgaID;
+
+ $sql = "
+SELECT
+ Organisationen.ID AS ID,
+ Organisationen.Name,
+ Organisationen.strasse,
+ Organisationen.plz,
+ Organisationen.ort,
+ Organisationen.adresszusatz,
+ Superorga.Name AS Superorganisation,
+ Superorga.ID AS SuperID
+FROM Organisationen
+LEFT JOIN Organisationen AS Superorga ON Organisationen.Superorganisation = Superorga.ID
+WHERE Organisationen.ID=?
+";
+ $stmt = $mysqli->prepare($sql);
+ $stmt->bind_param('i', $_GET['id']);
+ $stmt->execute();
+ $r = $stmt->get_result();
+ $l = $r->fetch_object();
+ $orgaID = $l->ID;
+ $superorgaID = $l->SuperorgaID;
+
+ $_title = 'Organisation: <span class="pii">' . $l->Name . '</span> (ID '. $_GET['id'] . ')';
+ include_once __DIR__ . "/header.php";
+?>
+
+<h1 style="display: inline-block;"><?=$_title?></h1>
+&nbsp;<button class="small" onclick="showNewNoteDialog({'orgaid': <?=$orgaID?>});"><i class="fas fa-sticky-note"></i> Notiz anlegen</button>
+&nbsp;<button class="small" style="background-color: red;" onclick="location.href='/db/main/delorga.php?id=<?=$orgaID?>';"><i class="fas fa-trash-alt"></i></sup> Datensatz löschen</button>
+
+<?
+ $id = "orga";
+ $def = [
+ "Organisation",
+ ['Name'],
+ [10, '=3strasse', '=3adresszusatz', 'plz', '=3ort'],
+ [10, '_SuperID', '_=9Superorganisation']
+ ];
+ $links = [
+ 'SuperID' => '/db/orga/'
+ ];
+ $entrytable = "Organisationen";
+
+ include __DIR__ . '/autoform.php';
+ $stmt->reset();
+})();
+
+(function () use ($orgaID) {
+ $globstring = "{" . $orgaID . "orga}*_";
+ require_once __DIR__ . '/doc.php';
+})();
+
+include_once __DIR__ . '/notesXorga.php';
+include_once __DIR__ . '/persXorga.php';
+include_once __DIR__ . '/donationsXorga.php';
+include_once __DIR__ . '/eventsXorga.php';
+
+require_once __DIR__ . "/jumper.php";
+include_once __DIR__ . '/footer.php';
diff --git a/orgaXpers.php b/orgaXpers.php
new file mode 100644
index 0000000..6f6f4a9
--- /dev/null
+++ b/orgaXpers.php
@@ -0,0 +1,49 @@
+<?php
+/*
+ * 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.
+ */
+?>
+<?
+/* vim: set ts=4 sw=4 et : */
+
+(function () use ($mysqli, $persID) {
+ if (!assureInt($persID)) return;
+
+ $title = "Organisationen zur Person";
+ $sql = "
+SELECT
+ Organisationen.ID AS OrgaID,
+ Organisationen.Name,
+ Ansprechpartner,
+ Funktion,
+ Abteilung
+FROM Personen_Organisationen
+LEFT JOIN Organisationen ON Personen_Organisationen.Organisation = Organisationen.ID
+WHERE Personen_Organisationen.Person=?
+";
+ $stmt = $mysqli->prepare($sql);
+ $stmt->bind_param('i', $persID);
+ $stmt->execute();
+ $r = $stmt->get_result();
+
+ $order = '[[2, "asc"], [0, "asc"]]';
+ $editable = [
+ 'Ansprechpartner',
+ 'Funktion',
+ 'Abteilung'
+ ];
+ $entrytable = 'Personen_Organisationen';
+ $types = ['Ansprechpartner' => 'checkbox'];
+
+ $nospinner = true;
+ include __DIR__ . '/autotable.php';
+ $stmt->reset();
+})();
diff --git a/orgapersons.php b/orgapersons.php
new file mode 100644
index 0000000..65ce0bd
--- /dev/null
+++ b/orgapersons.php
@@ -0,0 +1,142 @@
+<?php
+/*
+ * 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.
+ */
+?>
+<?php
+/* vim: set ts=4 sw=4 et : */
+
+$_title = "Personen bei Organisationen";
+
+require_once __DIR__ . "/check_auth.php";
+require_once __DIR__ . "/../includes/common.php";
+include_once __DIR__ . "/header.php";
+require_once __DIR__ . "/lookup.php";
+
+doTitle();
+
+(function () use ($mysqli) {
+ $sql = "
+SELECT
+ Personen_Organisationen.ID, Personen.ID AS PersID, Personen.Nachname, Personen.Vorname, Personen.Titel, Personen.Email, Organisationen.ID AS OrgaID, Organisationen.Name AS Organisation,
+ Personen_Organisationen.Funktion, Personen_Organisationen.Abteilung,
+
+ apstip, apsf, apop, apstr, apfi,
+ elsv, elfa, elsft, elosv,
+ ernl, erwk, erwm
+
+FROM Personen_Organisationen
+INNER JOIN Personen ON Personen_Organisationen.Person = Personen.ID
+INNER JOIN Organisationen ON Personen_Organisationen.Organisation = Organisationen.ID
+";
+ if (isset($_GET['persid'])) $sql .= " WHERE Personen.ID=?";
+ $stmt = $mysqli->prepare($sql);
+ if (isset($_GET['persid'])) $stmt->bind_param('i', $_GET['persid']);
+ $stmt->execute();
+ $r = $stmt->get_result();
+ $id = "orgapersons";
+ $order = '[[1, "asc"]]';
+ $types = [
+ 'apstip' => 'checkboxinverse',
+ 'apsf' => 'checkboxinverse',
+ 'apop' => 'checkboxinverse',
+ 'apstr' => 'checkboxinverse',
+ 'apfi' => 'checkboxinverse',
+
+ 'elsv' => 'checkboxinverse',
+ 'elfa' => 'checkboxinverse',
+ 'elsft' => 'checkboxinverse',
+ 'elosv' => 'checkboxinverse',
+
+ 'ernl' => 'checkboxinverse',
+ 'erwk' => 'checkboxinverse',
+ 'erwm' => 'checkboxinverse',
+
+ 'Ansprechpartner' => 'checkboxinverse',
+ 'Einladung_Stipendienvergabe' => 'checkboxinverse',
+ 'Einladung_SFT' => 'checkboxinverse',
+ 'Einladung_Fördereraustausch' => 'checkboxinverse',
+ ];
+ $editable = [
+ 'apstip',
+ 'apsf',
+ 'apop',
+ 'apstr',
+ 'apfi',
+
+ 'elsv',
+ 'elfa',
+ 'elsft',
+ 'elosv',
+
+ 'ernl',
+ 'erwk',
+ 'erwm',
+
+ 'Funktion',
+ 'Abteilung',
+
+ 'Ansprechpartner',
+ 'Einladung_Stipendienvergabe',
+ 'Einladung_SFT',
+ 'Einladung_Fördereraustausch',
+ ];
+ $entrytable = 'Personen_Organisationen';
+ $checkboxes = true;
+ $bottom = <<<EOD
+<div class="admin_actionsbelow">
+ Ausgewählte
+ <button class="medium" style="background: darkorange;" onclick="return delPO();"><i class="fas fa-trash-alt"></i> löschen</button>
+</div>
+<script>
+function delPO() {
+ $.ajaxSetup({async:false});
+ $.ajax({
+ type: 'POST',
+ url: '/db/main/delorgaperson.php',
+ data: {
+ 'ids': getIDs_$id()
+ }
+ });
+ $.ajaxSetup({async:true});
+ location.reload();
+ return false;
+}
+</script>
+EOD;
+ $pdf_sql = $email_sql = "
+SELECT
+ Organisationen.Name AS Orga, CONCAT(Organisationen.ID, 'orga-', Personen.ID, 'person') AS uid,
+ Organisationen.strasse AS Strasse, Organisationen.plz AS PLZ, Organisationen.ort AS Ort,
+ Personen.Geschlecht,
+ Personen.`informale Ansprache`,
+ Personen.Ansprache,
+ Personen.Anrede,
+ Personen.Titel,
+ Personen.Nachname,
+ Personen.Vorname,
+ Personen.Email,
+ Personen.Telefon,
+ Personen.Handy,
+ Personen_Organisationen.*
+FROM Personen_Organisationen
+INNER JOIN Personen ON Personen_Organisationen.Person = Personen.ID
+LEFT JOIN Organisationen ON Personen_Organisationen.Organisation = Organisationen.ID
+WHERE Personen_Organisationen.ID IN ";
+ include __DIR__ . '/autotable.php';
+ include __DIR__ . '/autoactions.php';
+ include __DIR__ . '/autoemail.php';
+ include __DIR__ . '/autopdf.php';
+ include __DIR__ . '/autoevent.php';
+ $stmt->reset();
+})();
+
+include_once __DIR__ . "/footer.php";
diff --git a/orgas.php b/orgas.php
new file mode 100644
index 0000000..90829aa
--- /dev/null
+++ b/orgas.php
@@ -0,0 +1,42 @@
+<?php
+/*
+ * 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.
+ */
+?>
+<?php
+/* vim: set ts=4 sw=4 et : */
+
+$_title = "Alle Organisationen";
+require_once __DIR__ . "/check_auth.php";
+include_once __DIR__ . "/header.php";
+require_once __DIR__ . "/../includes/common.php";
+?>
+
+<h1><?=$_title?></h1>
+
+<?php
+(function () use ($mysqli) {
+ $sql = "
+SELECT
+ Organisationen.ID AS OrgaID, Organisationen.Name, Organisationen.Ort,
+ Superorga.Name AS Superorganisation, Superorga.ID AS SuperOrgaID
+FROM Organisationen
+LEFT JOIN Organisationen AS Superorga ON Organisationen.Superorganisation = Superorga.ID
+";
+ $id = "orgas2";
+ $thdef = ['OrgaID', 'Name', 'Ort', 'SuperOrgaID', 'Superorganisation'];
+ $ajax = $nospinner = true;
+ $order = '[[1, "asc"]]';
+ include __DIR__ . '/autotable.php';
+})();
+
+include_once __DIR__ . "/footer.php";
+?>
diff --git a/patron.php b/patron.php
new file mode 100644
index 0000000..6a71ed3
--- /dev/null
+++ b/patron.php
@@ -0,0 +1,224 @@
+<?php
+/*
+ * 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.
+ */
+?>
+<?
+/* vim: set ts=4 sw=4 et : */
+
+require_once __DIR__ . "/check_auth.php";
+require_once __DIR__ . "/../includes/common.php";
+require_once __DIR__ . "/lookup.php";
+require_once __DIR__ . "/lookup_more.php";
+
+(function () use ($mysqli) {
+ global $foerdID, $persID, $orgaID;
+
+ $sql = "
+SELECT
+ Förderer.*,
+ Förderer.ID AS FoerdID,
+ Organisationen.Name AS Organisation,
+ Organisationen.ort, Organisationen.plz, Organisationen.strasse, Organisationen.adresszusatz,
+ Organisationen.ID AS OrgaID,
+ Zuständig AS ZustID,
+ DATE(Förderer.`Vertrag läuft aus`) AS `Vertrag läuft aus`,
+ zuwendungen AS APZuwID,
+ `Ansprechpartner Stipendiaten` AS APID,
+ `im Newsletter erwähnt`,
+ '' AS `A`,
+ matching
+FROM Förderer
+LEFT JOIN Organisationen ON Förderer.Organisation = Organisationen.ID
+WHERE Förderer.ID=?
+";
+ $stmt = $mysqli->prepare($sql);
+ $stmt->bind_param('i', $_GET['id']);
+ $stmt->execute();
+ $r = $stmt->get_result();
+ $l = $r->fetch_object();
+ $foerdID = $l->FoerdID;
+ $persID = $l->PersID;
+ $orgaID = $l->OrgaID;
+
+ $_title = 'Förderer: <span class="pii">' . $l->Organisation . '</span> (ID ' . $_GET['id'] . ')';
+ include_once __DIR__ . "/header.php";
+?>
+
+<h1 style="display: inline-block;"><?=$_title?></h1>
+&nbsp;<button class="small" onclick="showNewNoteDialog({'foerdid': <?=$foerdID?>, 'orgaid': <?=$orgaID?>});"><i class="fas fa-sticky-note"></i> Notiz anlegen</button>
+&nbsp;<button class="small" style="background-color: red;" onclick="location.href='/db/main/delpatron.php?id=<?=$foerdID?>';"><i class="fas fa-trash-alt"></i></sup> Datensatz löschen</button>
+
+<?
+ global $persXorga;
+ $sql = "SELECT Personen.ID, Personen.Nachname, Personen.Vorname FROM Personen_Organisationen INNER JOIN Personen ON Personen_Organisationen.Person = Personen.ID WHERE Personen_Organisationen.Organisation = ? ORDER BY Personen.Nachname";
+ $stmt = $mysqli->prepare($sql);
+ $stmt->bind_param('i', $orgaID);
+ $stmt->bind_result($id, $nachname, $vorname);
+ $stmt->execute();
+ $persXorga = [];
+ while ($stmt->fetch()) {
+ $persXorga[$id] = $nachname . ", " . $vorname;
+ }
+ $stmt->reset();
+
+ $id = "patron";
+ $entrytable = "Förderer";
+ $def = [
+ "Allgemein",
+ [10, '_OrgaID', '_=6Organisation', '=3legal|rechtsformen~Rechtsform'],
+ [10, '_A~ ', '_=3strasse', '_=3adresszusatz', '_plz', '_=2ort'],
+ [10, '_A~ ', '=9altname~Abweichende Fördererbezeichnung (als Auftritt gegenüber Stipendiaten)'],
+ [10, '_ZustID', '=4Zuständig|demo', '=5Kontakt hergestellt'],
+ "Allgemeine Vertragsbeziehungen",
+ [4, 'AnfrageWF#isodate~Anfrage (Weiter)-finanzierung', 'Mittelanforderung#isodate~Mittelanforderung vom', 'Annahmeanordnung#isodate~Annahmeanordnung vom', 'Zusage#isodate~Zusage Förderung am'],
+ [4, 'Vertrag läuft aus#isodate', '+im Newsletter erwähnt', '+SEPA-Lastschrift erteilt~SEPA', '+Zahlungsaufforderung gewünscht'],
+ [10, '_APZuwID', '=4zuwendungen|persXorga~Ansprechpartner Zuwendungen', '_APID', '=4Ansprechpartner Stipendiaten|persXorga~Erster Ansprechp./Danksagungskontakt für die Stipendiaten'],
+ ['Zahlungsanmerkung'],
+ ['*Widmung'],
+ ['*notes~Generelle Bemerkungen']
+ ];
+ include __DIR__ . '/autoform.php';
+ $stmt->reset();
+})();
+
+(function () use ($foerdID, $orgaID) {
+ $globstring = "{" . $foerdID . "patron," . $orgaID . "orga}*_";
+ require_once __DIR__ . '/doc.php';
+})();
+
+(function () use ($foerdID) {
+ if (!assureInt($foerdID)) return;
+
+ $id = "contractsXpatron3";
+ $title = "Verträge zum Förderer";
+ $off = true;
+ $sql_a = "
+SELECT contracts.id AS VertrID, CONCAT(calls.name, ' [', `call`, ']') AS 'Call', ls AS Stipendien, remark, ss AS Sozial, (ls * 12 * 150 + ss * 12 * 150) AS Summe,
+CONCAT(IF(Förderer.`SEPA-Lastschrift erteilt`, 'SEPA|', ''), IF(SUM(Spenden.Betrag) > 0, SUM(Spenden.Betrag), 0)) AS Eingang,
+IF(Förderer.`SEPA-Lastschrift erteilt`, 0, ((150 * contracts.ss + 150 * contracts.ls) * 12) - IF(SUM(Spenden.Betrag) > 0, SUM(Spenden.Betrag), 0)) AS Fehlbetrag,
+IF(GROUP_CONCAT(DATE(Spenden.Geldeingang) SEPARATOR ', ') IS NULL, CONCAT(GROUP_CONCAT(DATE(SuperSpenden.Geldeingang) SEPARATOR ', '), '*'), GROUP_CONCAT(DATE(Spenden.Geldeingang) SEPARATOR ', ')) AS Geldeingänge,
+contact AS PersID, CONCAT(Personen.Nachname, ', ', Personen.Vorname) AS `Ansprechpartner`, valid_from AS `Gültig von`, valid_to AS `Gültig bis`
+FROM contracts
+LEFT JOIN Personen ON contracts.contact = Personen.ID
+LEFT JOIN calls ON contracts.`call` = calls.shorthand
+LEFT JOIN Förderer ON contracts.patron = Förderer.ID
+LEFT JOIN Spenden ON contracts.id = Spenden.contract
+LEFT JOIN Spenden AS SuperSpenden ON Spenden.SuperID = SuperSpenden.ID
+";
+ $sql = $sql_a . ' WHERE contracts.patron=' . $foerdID;
+ $sql .= " GROUP BY contracts.id ";
+ $thdef = ['VertrID', 'Call', 'Stipendien', 'remark', 'Summe', 'Eingang', 'Fehlbetrag', 'Geldeingänge', 'PersID', 'Ansprechpartner', 'Gültig von', 'Gültig bis'];
+ $order = '[[2, "desc"], [5, "desc"]]';
+ $checkboxes = true;
+ $idcell = "VertrID";
+ $bottom = <<<EOD
+<div class="admin_actionsbelow">
+ Ausgewählte
+ <button class="medium" style="background: darkorange;" onclick="return delContractPatron();"><i class="fas fa-trash-alt"></i> löschen</button>
+</div>
+<script>
+function delContractPatron() {
+ $.ajaxSetup({async:false});
+ $.ajax({
+ type: 'POST',
+ url: '/db/main/delcontract.php',
+ data: {
+ 'ids': getIDs_$id()
+ }
+ });
+ $.ajaxSetup({async:true});
+ location.reload();
+ return false;
+}
+</script>
+EOD;
+ $ajax = $nospinner = true;
+ include __DIR__ . '/autotable.php';
+})();
+
+include_once __DIR__ . '/notesXpatron.php';
+
+(function () use ($foerdID) {
+ if (!assureInt($foerdID)) return;
+
+ $id = "stipsXpatron_Patron";
+ $title = "Stipendien zum Förderer";
+ $off = true;
+ $sql_a = "
+SELECT
+ Stipendien.ID AS StipID,
+ Personen.Nachname, Personen.Vorname, Personen.ID AS PersID, Personen.Email,
+ Stipendien.Jahr,
+ CONCAT(DATE(Stipendien.Förderbeginn), ' &ndash; ', DATE(Stipendien.Förderende)) AS Zeitraum,
+ HSOrga.Name AS Hochschule,
+ Studiengänge.Name AS Studiengang,
+ Förderarten.Name AS Förderart
+FROM Stipendien
+LEFT JOIN Förderer ON Stipendien.Förderer = Förderer.ID
+LEFT JOIN Organisationen ON Förderer.Organisation = Organisationen.ID
+LEFT JOIN Hochschulen ON Stipendien.Hochschule = Hochschulen.ID
+LEFT JOIN Organisationen AS HSOrga ON Hochschulen.Organisation = HSOrga.ID
+LEFT JOIN Studiengänge ON Stipendien.Studiengang = Studiengänge.ID
+LEFT JOIN Förderarten ON Stipendien.Förderart = Förderarten.ID
+LEFT JOIN Personen ON Stipendien.Person = Personen.ID
+";
+ $sql = $sql_a . ' WHERE Stipendien.Förderer=' . $foerdID;
+ $order = '[[6, "desc"], [2, "asc"], [3, "asc"]]';
+ $thdef = ['StipID', 'Nachname', 'Vorname', 'PersID', 'Email', 'Jahr', 'Zeitraum', 'Hochschule', 'Studiengang', 'Förderart'];
+ $checkboxes = true;
+ $idcell = "StipID";
+ $ajax = $nospinner = true;
+ include __DIR__ . '/autotable.php';
+
+ $email_sql = preg_replace('/^\s*SELECT\s/', 'SELECT Stipendien.ID AS RUID, ', $sql_a) . ' WHERE Stipendien.ID IN ';
+ include __DIR__ . '/autoactions.php';
+ include __DIR__ . '/autoemail.php';
+ include __DIR__ . '/autoevent.php';
+})();
+?>
+
+<span id="insert_persXorga"></span>
+
+<?php
+(function () use ($foerdID) {
+ if (!assureInt($foerdID)) return;
+
+ $id = "donationsXcontractsXpatron2";
+ $title = "Spenden zu Verträgen des Förderers";
+ $sql = "
+SELECT
+ Spenden.ID AS SpendenID,
+ Spenden.Betrag,
+ DATE(Spenden.Geldeingang) AS Geldeingang,
+ Spenden.contract AS VertrID,
+ IF(Spenden.contract > 0, CONCAT(contracts.`call`, '-', Organisationen.Name), '') AS Vertrag,
+ Spenden.Person AS PersID,
+ Personen.Nachname,
+ Personen.Vorname
+FROM Spenden
+LEFT JOIN Personen ON Spenden.Person = Personen.ID
+LEFT JOIN contracts ON Spenden.contract = contracts.ID
+LEFT JOIN Förderer ON contracts.patron = Förderer.ID
+LEFT JOIN Organisationen ON Förderer.Organisation = Organisationen.ID
+WHERE contracts.patron=" . $foerdID;
+ $order = '[[1, "desc"]]';
+ $thdef = ['SpendenID', 'Betrag', 'Geldeingang', 'VertrID', 'Vertrag', 'PersID', 'Nachname', 'Vorname'];
+ $ajax = $nospinner = true;
+ include __DIR__ . '/autotable.php';
+})();
+
+include_once __DIR__ . '/donationsXorga.php';
+include_once __DIR__ . '/eventsXorga.php';
+include_once __DIR__ . '/persXorga.php';
+
+require_once __DIR__ . "/jumper.php";
+include_once __DIR__ . '/footer.php';
diff --git a/patrons.php b/patrons.php
new file mode 100644
index 0000000..12d2901
--- /dev/null
+++ b/patrons.php
@@ -0,0 +1,19 @@
+<?php
+/*
+ * 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.
+ */
+?>
+<?php
+/* vim: set ts=4 sw=4 et : */
+
+$_title = "Alle Förderer <small>(unabhängig vom Status)</small>";
+$_constraint = " GROUP BY Förderer.ID";
+require_once __DIR__ . "/patrons_common.php";
diff --git a/patrons_by_year.php b/patrons_by_year.php
new file mode 100644
index 0000000..2f4050c
--- /dev/null
+++ b/patrons_by_year.php
@@ -0,0 +1,41 @@
+<?php
+/*
+ * 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.
+ */
+?>
+<?php
+/* vim: set ts=4 sw=4 et : */
+
+$_year = $_GET['year'];
+$_title = "";
+
+$social = "";
+if (isset($_GET['social'])) {
+ $social = "AND contracts.ss > 0";
+ $_title .= "Sozialstipendiums-";
+}
+
+$ds = "";
+if (isset($_GET['ds'])) {
+ $ds = "AND contracts.ls > 0";
+ $_title .= "Leistungstipendiums-";
+}
+
+$ideell = "";
+if (isset($_GET['ideell'])) {
+ $ds = "AND (contracts.ls < 1 AND contracts.ss < 1)";
+ $_title .= "Ideellen-";
+}
+
+$_title .= "Förderer im Förderjahr " . $_year . "/" . ($_year + 1) . " <small>(anhand Vertr&auml;ge)</small>";
+
+$_constraint = "WHERE (contracts.valid_from >= '" . $_year . "-10-01' AND contracts.valid_from < '" . ($_year + 1) . "-10-01' AND contracts.id > 0) " . $social . " " . $ds . " " . $ideell . " GROUP BY Förderer.ID";
+require_once __DIR__ . "/patrons_common.php";
diff --git a/patrons_common.php b/patrons_common.php
new file mode 100644
index 0000000..b01153b
--- /dev/null
+++ b/patrons_common.php
@@ -0,0 +1,155 @@
+<?php
+/*
+ * 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.
+ */
+?>
+<?php
+/* vim: set ts=4 sw=4 et : */
+
+require_once __DIR__ . "/check_auth.php";
+require_once __DIR__ . "/../includes/common.php";
+include_once __DIR__ . "/header.php";
+require_once __DIR__ . "/lookup.php";
+
+doTitle();
+
+(function () use ($mysqli, $_constraint, $_year) {
+ $sql = "
+SELECT
+ Förderer.ID AS FoerdID,
+ Organisationen.ID AS OrgaID,
+ Organisationen.Name AS Organisation,
+ Organisationen.strasse AS Straße,
+ Organisationen.plz AS PLZ,
+ Organisationen.ort AS Ort,
+ SUM(contracts.ls) AS 'Anz. LS',
+ Zahlungsanmerkung,
+ Förderer.Widmung,
+ Förderer.notes AS Bemerkungen,
+ `Ansprechpartner Stipendiaten` AS APID,
+ CONCAT(APerson.Nachname, ', ', APerson.Vorname) AS `Erster Ansprechp./Kontakt für die Stips`,
+ Förderer.zuwendungen AS APZuwID,
+ CONCAT(AZuwPerson.Nachname, ', ', AZuwPerson.Vorname) AS `Ansprechpartner Zuwendung`,
+ AnfrageWF,
+ Mittelanforderung,
+ Annahmeanordnung,
+ Zusage,
+ `Kontakt hergestellt`,
+ legal.name AS Rechtsform,
+ `SEPA-Lastschrift erteilt`,
+ `im Newsletter erwähnt`,
+ `Zahlungsaufforderung gewünscht`,
+
+ (SELECT COUNT(*) FROM Personen_Organisationen WHERE Personen_Organisationen.Organisation = Organisationen.ID AND Personen_Organisationen.apstip=1) AS '#apstip',
+ (SELECT COUNT(*) FROM Personen_Organisationen WHERE Personen_Organisationen.Organisation = Organisationen.ID AND Personen_Organisationen.apsf=1) AS '#apsf',
+ (SELECT COUNT(*) FROM Personen_Organisationen WHERE Personen_Organisationen.Organisation = Organisationen.ID AND Personen_Organisationen.apop=1) AS '#apop',
+ (SELECT COUNT(*) FROM Personen_Organisationen WHERE Personen_Organisationen.Organisation = Organisationen.ID AND Personen_Organisationen.apstr=1) AS '#apstr',
+ (SELECT COUNT(*) FROM Personen_Organisationen WHERE Personen_Organisationen.Organisation = Organisationen.ID AND Personen_Organisationen.apfi=1) AS '#apfi',
+ (SELECT COUNT(*) FROM Personen_Organisationen WHERE Personen_Organisationen.Organisation = Organisationen.ID AND Personen_Organisationen.elsv=1) AS '#elsv',
+ (SELECT COUNT(*) FROM Personen_Organisationen WHERE Personen_Organisationen.Organisation = Organisationen.ID AND Personen_Organisationen.elfa=1) AS '#elfa',
+ (SELECT COUNT(*) FROM Personen_Organisationen WHERE Personen_Organisationen.Organisation = Organisationen.ID AND Personen_Organisationen.elsft=1) AS '#elsft',
+ (SELECT COUNT(*) FROM Personen_Organisationen WHERE Personen_Organisationen.Organisation = Organisationen.ID AND Personen_Organisationen.elosv=1) AS '#elosv',
+ (SELECT COUNT(*) FROM Personen_Organisationen WHERE Personen_Organisationen.Organisation = Organisationen.ID AND Personen_Organisationen.ernl=1) AS '#ernl',
+ (SELECT COUNT(*) FROM Personen_Organisationen WHERE Personen_Organisationen.Organisation = Organisationen.ID AND Personen_Organisationen.erwk=1) AS '#erwk',
+ (SELECT COUNT(*) FROM Personen_Organisationen WHERE Personen_Organisationen.Organisation = Organisationen.ID AND Personen_Organisationen.erwm=1) AS '#erwm'
+
+FROM Förderer
+LEFT JOIN Organisationen ON Förderer.Organisation = Organisationen.ID
+LEFT JOIN Personen ON Förderer.Zuständig = Personen.ID
+LEFT JOIN Personen AS AZuwPerson ON Förderer.zuwendungen = AZuwPerson.ID
+LEFT JOIN Personen AS APerson ON Förderer.`Ansprechpartner Stipendiaten` = APerson.ID
+LEFT JOIN legal ON Förderer.legal = legal.id
+LEFT JOIN contracts ON Förderer.ID = contracts.patron
+";
+ $sql .= $_constraint;
+ $stmt = $mysqli->prepare($sql);
+ $stmt->execute();
+ $r = $stmt->get_result();
+ $id = "patrons3";
+ $order = '[[3, "asc"]]';
+ $entrytable = 'Förderer';
+ $idcell = "FoerdID";
+ $idcellreal = "ID";
+ $types = [
+ 'Bewerbungsunterlagen verschickt' => 'checkbox',
+ 'gemeinsames Foto' => 'checkbox',
+ 'Pressemitteilung' => 'checkbox',
+ 'Im Matching-Tool eingetragen' => 'checkbox',
+ 'SEPA-Lastschrift erteilt' => 'checkbox',
+ 'Zahlungsaufforderung gewünscht' => 'checkbox',
+ 'im Newsletter erwähnt' => 'checkbox',
+ 'AnfrageWF' => 'date',
+ 'Annahmeanordnung' => 'date',
+ 'Mittelanforderung' => 'date',
+ 'Zusage' => 'date',
+ ];
+ $editable = [
+ 'Bewerbungsunterlagen verschickt',
+ 'gemeinsames Foto',
+ 'Pressemitteilung',
+ 'bezahlt bis',
+ 'Betrag bezahlt',
+ 'Vertrag läuft aus',
+ 'Anzahl Leistungsstipendien',
+ 'Anzahl Sozialstipendien',
+ 'Zahlungsanmerkung',
+ 'Kontakt hergestellt',
+ 'Im Matching-Tool eingetragen',
+ 'SEPA-Lastschrift erteilt',
+ 'Zahlungsaufforderung gewünscht',
+ 'im Newsletter erwähnt',
+ 'AnfrageWF',
+ 'Mittelanforderung',
+ 'Annahmeanordnung',
+ 'Zusage',
+ ];
+ $checkboxes = true;
+ include __DIR__ . '/autotable.php';
+ $stmt->reset();
+
+ $email_sql = "
+SELECT Förderer.ID AS FoerdID, Organisationen.Name AS Foerderer, CONCAT(Förderer.ID, 'patron', '-', Personen.ID, 'person') AS uid, Personen.*, Organisationen.strasse AS Strasse, Organisationen.plz AS PLZ, Organisationen.ort AS Ort, Organisationen.Name AS Organisation, ROUND(Förderer.`Betrag bezahlt`, 0) AS Betrag, CONCAT(Förderer.ID, 'patron', '-', Personen.ID, 'person') AS genuid
+FROM Förderer
+LEFT JOIN Organisationen ON Förderer.Organisation = Organisationen.ID
+ ";
+ if ($_GET['head']) {
+ $email_sql .= " LEFT JOIN Personen ON Förderer.`Ansprechpartner Stipendiaten` = Personen.ID ";
+ } else {
+ $email_sql .= " LEFT JOIN Personen ON Förderer.`Ansprechpartner Stipendiaten` = Personen.ID ";
+ }
+ $email_sql .= " WHERE Förderer.ID IN ";
+ $pdf_sql = $email_sql;
+ $pdf_file = "anschreiben_katja";
+ $payload_sql = "SELECT Personen.ID AS PersID, Förderer.ID AS FoerdID, Personen.Geschlecht, Personen.`informale Ansprache`, Personen.Anrede, Personen.Ansprache, Personen.Titel, Personen.Vorname AS Vorname, Personen.Nachname AS Nachname, Personen_Organisationen.* FROM Personen_Organisationen LEFT JOIN Förderer ON Personen_Organisationen.Organisation = Förderer.Organisation LEFT JOIN Personen ON Personen_Organisationen.Person = Personen.ID WHERE Förderer.ID IN ";
+ $ipyear = 2023;
+ if (isset($_year) && $_year > 2000) {
+ $ipyear = intval($_year);
+ }
+ $independent_payload_sql = "
+SELECT
+ Stipendien.ID AS StipID, Stipendien.Förderer AS FoerdID, Stipendien.Förderbeginn AS Foerderbeginn, Stipendien.Förderende AS Foerderende,
+ Stipendien.Förderart AS Foerderart, Hochschulen.KanonischerName AS Hochschule,
+ Studiengänge.Name AS Studiengang,
+ ORD(`Weitergabe Daten an Förderer`) AS Weitergabe, Personen.Nachname, Personen.Vorname, Personen.Email, Personen.Geschlecht,
+ IF(event_participants.validated=1 AND event_participants.declined=0, 1, 0) AS stipvergabe
+FROM Stipendien
+INNER JOIN Personen ON Stipendien.Person = Personen.ID
+INNER JOIN Hochschulen ON Stipendien.Hochschule = Hochschulen.ID
+INNER JOIN Studiengänge ON Stipendien.Studiengang = Studiengänge.ID
+LEFT JOIN event_participants ON Personen.ID = event_participants.persid AND 598 = event_participants.eventid
+WHERE Jahr IN (" . $ipyear .") AND Förderart NOT IN (2, 6)
+ORDER BY Personen.Nachname, Personen.Vorname;";
+ include __DIR__ . '/autoactions.php';
+ include __DIR__ . '/autoemail.php';
+ include __DIR__ . '/autopdf.php';
+})();
+
+include_once __DIR__ . "/footer.php";
diff --git a/patronspersons.php b/patronspersons.php
new file mode 100644
index 0000000..e899b47
--- /dev/null
+++ b/patronspersons.php
@@ -0,0 +1,28 @@
+<?php
+/*
+ * 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.
+ */
+?>
+<?php
+/* vim: set ts=4 sw=4 et : */
+
+$_title = "Alle Personen bei aktuellen Förderern <small>(anhand Vertr&auml;ge)</small>";
+$_subtitle = '<div style="max-width: 90ch; line-height: .7em;"><small>Als "aktueller" bzw. "zukünftiger" Förderer wird in dieser Abfrage jeder Förderer angesehen, der einen Vertrag zugewiesen hat, welcher zum aktuellen Zeitpunkt oder in der Zukunft gültig sein wird (anhand "gültig von"/"bis"). Zusätzlich kann ggf. noch auf die Ausschreibung (Call) eingegrenzt werden.</small></div>';
+
+$call = '%';
+if (isset($_GET['call']) && $_GET['call'] != '') {
+ $call = filter_var($_GET['call'], FILTER_SANITIZE_STRING);
+ $_title .= " <small>(Call " . $call . ")</small>";
+}
+
+$_constraint = "WHERE Förderer.ID IN (SELECT contracts.patron FROM contracts WHERE (contracts.valid_from <= NOW()) AND (contracts.valid_to >= NOW()) AND (contracts.`call` LIKE '" . $call . "'))";
+
+require __DIR__ . "/patronspersons_common.php";
diff --git a/patronspersons_by_year.php b/patronspersons_by_year.php
new file mode 100644
index 0000000..ad3ae4c
--- /dev/null
+++ b/patronspersons_by_year.php
@@ -0,0 +1,42 @@
+<?php
+/*
+ * 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.
+ */
+?>
+<?php
+/* vim: set ts=4 sw=4 et : */
+
+$_year = $_GET['year'];
+$fragment = "";
+
+$social = "";
+if (isset($_GET['social'])) {
+ $social = "AND contracts.ss > 0";
+ $fragment .= "Sozialstipendiums-";
+}
+
+$ds = "";
+if (isset($_GET['ds'])) {
+ $ds = "AND contracts.ls > 0";
+ $fragment .= "Leistungstipendiums-";
+}
+
+$ideell = "";
+if (isset($_GET['ideell'])) {
+ $ds = "AND (contracts.ls < 1 AND contracts.ss < 1)";
+ $fragment .= "Idellen-";
+}
+
+$_title = "Alle Personen bei " . $fragment . "Förderern des Förderjahres " . $_year . "/" . ($_year + 1) . " <small>(anhand Vertr&auml;ge)</small>";
+
+$_constraint = "WHERE Förderer.ID IN (SELECT contracts.patron FROM contracts WHERE (contracts.valid_from >= '" . $_year . "-10-01') AND (contracts.valid_from < '" . ($_year + 1) . "-10-01') " . $social . " " . $ds . " " . $ideell . ")";
+
+require __DIR__ . "/patronspersons_common.php";
diff --git a/patronspersons_common.php b/patronspersons_common.php
new file mode 100644
index 0000000..8a24b51
--- /dev/null
+++ b/patronspersons_common.php
@@ -0,0 +1,115 @@
+<?php
+/*
+ * 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.
+ */
+?>
+<?php
+/* vim: set ts=4 sw=4 et : */
+
+require_once __DIR__ . "/check_auth.php";
+require_once __DIR__ . "/../includes/common.php";
+include_once __DIR__ . "/header.php";
+
+doTitle();
+
+(function () use ($_constraint, $_year) {
+ $sql = "
+SELECT
+ Personen_Organisationen.ID AS PersOrgaID,
+ Personen.ID AS PersID, Personen.Nachname, Personen.Vorname,
+ Förderer.ID AS FoerdID, Organisationen.Name AS Förderer,
+ legal.name AS Rechtsform,
+ PersonenSFOWL.Nachname AS Zuständig,
+
+ Personen_Organisationen.Funktion, Personen_Organisationen.Abteilung,
+
+ Personen_Organisationen.apstip,
+ Personen_Organisationen.apsf,
+ Personen_Organisationen.apop,
+ Personen_Organisationen.apstr,
+ Personen_Organisationen.apfi,
+ Personen_Organisationen.elsv,
+ Personen_Organisationen.elfa,
+ Personen_Organisationen.elsft,
+ Personen_Organisationen.elosv,
+ Personen_Organisationen.ernl,
+ Personen_Organisationen.ernl,
+ Personen_Organisationen.erwk,
+ Personen_Organisationen.erwm,
+ IF(Förderer.`Ansprechpartner Stipendiaten` = Personen.ID, '1', '0') AS `Erster AP/Danksagung Stips`,
+
+ Personen.Ort, Personen.Email, Personen.Telefon, Personen.Handy,
+ (Personen_Organisationen.apop=1 OR Personen_Organisationen.apstr=1 OR Personen_Organisationen.apstip=1) AS `ap-op-str-stip`,
+ IF(event_participants.validated=1 AND event_participants.declined=0, 1, 0) AS Event631
+FROM Personen
+INNER JOIN Personen_Organisationen ON Personen.ID = Personen_Organisationen.Person
+LEFT JOIN Organisationen ON Personen_Organisationen.Organisation = Organisationen.ID
+LEFT JOIN Förderer ON Organisationen.ID = Förderer.Organisation
+LEFT JOIN Personen AS PersonenSFOWL ON Förderer.zuständig = PersonenSFOWL.ID
+LEFT JOIN legal ON Förderer.legal = legal.id
+LEFT JOIN event_participants ON Personen.ID = event_participants.persid AND event_participants.eventid=631
+";
+ $sql .= $_constraint;
+ $id = "patronspersons_common";
+ $getthdef = true;
+ $order = '[[3, "asc"], [4, "asc"]]';
+ $checkboxes = true;
+ $ajax = $nospinner = true;
+ include __DIR__ . '/autotable.php';
+
+ $pdf_sql = $email_sql = "
+SELECT Organisationen.Name AS Foerderer, CONCAT(Förderer.ID, 'patron-', Personen.ID, 'person') AS uid,
+ Organisationen.strasse AS Strasse, Organisationen.plz AS PLZ, Organisationen.ort AS Ort,
+ Personen.Geschlecht,
+ Personen.`informale Ansprache`,
+ Personen.Ansprache,
+ Personen.Anrede,
+ Personen.Titel,
+ Personen.Nachname,
+ Personen.Vorname,
+ Personen.Email,
+ Personen.Telefon,
+ Personen.Handy,
+ Personen.ID AS PersID,
+ Förderer.ID AS FoerdID
+FROM Personen
+INNER JOIN Personen_Organisationen ON Personen.ID = Personen_Organisationen.Person
+LEFT JOIN Organisationen ON Personen_Organisationen.Organisation = Organisationen.ID
+LEFT JOIN Förderer ON Organisationen.ID = Förderer.Organisation
+WHERE Personen_Organisationen.ID IN ";
+ $payload_sql = "
+SELECT eventid, persid, token FROM event_participants WHERE eventid=631 AND '999999999' NOT IN
+";
+ $ipyear = 2023;
+ if (isset($_year) && $_year > 2000) {
+ $ipyear = intval($_year);
+ }
+ $independent_payload_sql = "
+SELECT
+ Stipendien.ID AS StipID, Stipendien.Förderer AS FoerdID, Stipendien.Förderbeginn AS Foerderbeginn, Stipendien.Förderende AS Foerderende,
+ Stipendien.Förderart AS Foerderart, Hochschulen.KanonischerName AS Hochschule,
+ Studiengänge.Name AS Studiengang,
+ ORD(`Weitergabe Daten an Förderer`) AS Weitergabe, Personen.Nachname, Personen.Vorname, Personen.Email, Personen.Geschlecht,
+ IF(event_participants.validated=1 AND event_participants.declined=0, 1, 0) AS stipvergabe
+FROM Stipendien
+INNER JOIN Personen ON Stipendien.Person = Personen.ID
+INNER JOIN Hochschulen ON Stipendien.Hochschule = Hochschulen.ID
+INNER JOIN Studiengänge ON Stipendien.Studiengang = Studiengänge.ID
+LEFT JOIN event_participants ON Personen.ID = event_participants.persid AND 598 = event_participants.eventid
+WHERE Jahr IN (" . $ipyear . ") AND Förderart NOT IN (2, 6)
+ORDER BY Personen.Nachname, Personen.Vorname;";
+ include __DIR__ . '/autoactions.php';
+ include __DIR__ . '/autoemail.php';
+ include __DIR__ . '/autopdf.php';
+ include __DIR__ . '/autoevent.php';
+})();
+
+include_once __DIR__ . "/footer.php";
diff --git a/pdf.php b/pdf.php
new file mode 100644
index 0000000..7c912ea
--- /dev/null
+++ b/pdf.php
@@ -0,0 +1,516 @@
+<?php
+/*
+ * 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.
+ */
+?>
+<?
+/* vim: set ts=4 sw=4 et : */
+
+$_title = "PDF-Dokumente generieren";
+
+require_once __DIR__ . "/check_auth.php";
+require_once __DIR__ . "/../includes/common.php";
+
+require_once __DIR__ . "/lookup.php";
+
+require_once __DIR__ . "/header.php";
+
+if (isset($_GET['demo'])) $_SESSION['demo'] = true;
+
+session_write_close();
+
+$num = 0;
+if ($_POST['ids'] != '')
+ $num = count(explode(',', $_POST['ids']));
+?>
+
+<div id="pdf_modal" style="display: none;" class="transparent_modal">
+ <button onclick="abortAll()" class="emergency_off">NOT-AUS</button>
+ <div class="sk-folding-cube hvcenter">
+ <div class="sk-cube1 sk-cube"></div>
+ <div class="sk-cube2 sk-cube"></div>
+ <div class="sk-cube4 sk-cube"></div>
+ <div class="sk-cube3 sk-cube"></div>
+ </div>
+</div>
+
+<h1>PDF-Dokumente generieren
+<? if ($num > 1) { ?>
+<span style="font-size: .5em;">f&uuml;r <?=$num?> Datens&auml;tze</span>
+<? } else { ?>
+<span style="font-size: .5em;">f&uuml;r <?=$num?> Datensatz</span>
+<? } ?>
+</h1>
+
+<? if ($_SESSION['demo']) { ?>
+<p>Demo-Flag ist an</p>
+<? } ?>
+
+<form id="getpdf" action="/db/main/dopdf.php?test" target="_blank" method="post" style="display: inline-block;">
+<table border="0" style="display: inline-block;" class="consignee">
+ <tr>
+ <td>Organisation<sup><small>Vä</small></sup></td>
+ <td colspan="3"><input type="text" style="width: 530px;" class="changeable" name="organisation" value=""></td>
+ </tr>
+ <tr>
+ <td>Anrede|Briefkopf<sup><small>V</small></sup></td>
+ <td><input type="text" readonly name="anrede" style="width: 12ch;" value=""> | <input type="text" readonly name="anrede_briefkopf" style="width: 12ch;" value=""></td>
+ <td>Titel<sup><small>V</small></sup></td>
+ <td><input type="text" readonly name="titel" value=""></td>
+ </tr>
+ <tr>
+ <td>Vorname<sup><small>V</small></sup></td>
+ <td><input type="text" readonly name="vorname" value=""></td>
+ <td>Nachname<sup><small>V</small></sup></td>
+ <td><input type="text" readonly name="nachname" value=""></td>
+ </tr>
+ <tr>
+ <td>Straße<sup><small>Vä</small></sup></td>
+ <td><input type="text" class="changeable" name="strasse" value=""></td>
+ <td>Adresszusatz<sup><small>Vä</small></sup></td>
+ <td><input type="text" class="changeable" name="adresszusatz" value=""></td>
+ </tr>
+ <tr>
+ <td>PLZ<sup><small>Vä</small></sup></td>
+ <td><input type="text" class="changeable" name="plz" value=""></td>
+ <td>Ort<sup><small>Vä</small></sup></td>
+ <td><input type="text" class="changeable" name="ort" value=""></td>
+ </tr>
+ <tr>
+ <td colspan="4" style="padding-top: 0;"><small>V: Vorschau, ä: änderbar</small></td>
+ </tr>
+ <tr>
+ <td>Dokument</td>
+ <td colspan="3">
+ <select class="mand tchange" name="fn" style="width: 530px;">
+<? _lookup_pdf_documents(); foreach ($pdf_documents as $d) { ?>
+<option value="<?=$d['name']?>"<? if ($d['name'] == $_POST['file']) { ?> selected<? }?>><?=$d['name']?> - <?=$d['description']?></option>
+<? } ?>
+ <option value="" style="color: red !important;"><span style="color: red !important;">Bitte Auswahl treffen!</span></option>
+<? if (false) { ?><input type="text" readonly name="fn" value="<?=$_POST['file']?>"><? } ?>
+ </select>
+ </td>
+ </tr>
+ <tr>
+ <td>Betreff</td>
+ <td colspan="3"><input style="width: 530px;" class="mand tchange" type="text" name="subject" placeholder="Bescheid fuer..."></td>
+ </tr>
+ <tr>
+ <td>Datum <button style="padding: 0; margin: 0; font-size: .8em;" onclick="$('#templdate').val($.datepicker.formatDate('dd.mm.yy', new Date())); return false;">heute</button></td>
+ <td><input class="mand tchange" type="text" id="templdate" name="date" placeholder="01.10.2019"></td>
+ </tr>
+</table>
+<input type="hidden" name="template" type="text" value="">
+<input type="hidden" name="html" type="text" value="">
+<input type="hidden" name="data" type="text" value="">
+<input type="hidden" name="uid" type="text" value="">
+</form>
+
+<div style="display: inline-block; margin-left: 1em; vertical-align: top;">
+ <button onclick="return sendAll();" class="medium" style="width: 12em;"><i class="fas fa-file-pdf"></i><i class "fas fa-folder-open"></i> alle generieren</button><br />
+ <button onclick="return sendOne();" class="medium" style="width: 12em;"><i class="fas fa-file-pdf"></i><i class="fas fa-save"></i> dieses generieren</button><br />
+ <button onclick="return getOne();" class="medium" style="width: 12em;"><i class="fas fa-file-pdf"></i><i class="fas fa-question"></i> dieses testen</button>
+
+ <div style="color: darkorange; margin-top: .5em;">
+ Letzter Status:
+ <p id="pdf_status"></p>
+ </div>
+</div>
+
+<div style="display: inline-block; margin-left: 1em; vertical-align: top;">
+ <b>Templates</b>
+ <div style="color: darkorange; margin-top: .5em;">
+<?
+ $sql = "SELECT id, name, text, subject, date, fn, ts FROM pdf_templates ORDER BY name";
+ $stmt = $mysqli->prepare($sql);
+ $stmt->bind_result($id, $name, $text, $subject, $date, $fn, $ts);
+ $stmt->execute();
+?>
+ <select id="pdf_template" style="max-width: 500px;">
+ <option data-text="" data-subject="" data-date="" value="" selected></option>
+<?
+ $templates = [];
+ while ($stmt->fetch()) {
+ $templates[$id] = ['name' => $name, 'text' => $text, 'ts' => $ts];
+?>
+ <option data-id="<?=$id?>"
+ data-name="<?=str_replace("\"", "&quot;", $name)?>"
+ data-subject="<?=str_replace("\"", "&quot;", $subject)?>"
+ data-date="<?=str_replace("\"", "&quot;", $date)?>"
+ data-text="<?=str_replace("\"", "&quot;", $text)?>"
+ data-fn="<?=str_replace("\"", "&quot;", $fn)?>"
+ value="<?=$id?>"><?=$name?> (ID <?=$id?>; <?=$ts?>)</option>
+<?
+ }
+?>
+ </select>
+ <br />
+ <br />
+ <button class="small" onclick="return loadPDFTemplate();"><i class="fas fa-upload"></i> Laden</button>
+ <button class="small" onclick="return savePDFTemplate();" style="background: orange"><i class="fas fa-download"></i> &Uuml;berschreiben</button>
+ <button class="small" onclick="return newPDFTemplate();"><i class="far fa-file"></i> Neu</button>
+ <button class="small" onclick="return renamePDFTemplate();"><i class="fas fa-pencil-alt"></i> Umbenennen</button>
+ <button class="small" onclick="return delPDFTemplate();" style="background: red;"><i class="fas fa-trash"></i> L&ouml;schen</button>
+<?
+ $stmt->reset();
+?>
+
+ </div>
+</div>
+
+<script>
+<? minStart(); ?>
+
+$('#pdf_template').on('change', function () {
+ $('#pdf_template').css('background', 'white');
+});
+
+$('.tchange').on('input change cut copy paste', function () {
+ $('#pdf_template').css('background', 'lightgrey');
+});
+
+$(document).ready(function () {
+ quill.on('text-change', function () {
+ $('#pdf_template').css('background', 'lightgrey');
+ });
+
+ setTimeout(function () {
+ var tid = localStorage.getItem('pdf_template');
+ if (tid !== '' && tid > 0) {
+ $('select#pdf_template').val(tid);
+ localStorage.removeItem('pdf_template');
+ setTimeout(function () {
+ loadPDFTemplate();
+ }, 100);
+ }
+ }, 200);
+
+ $('select[name=fn]').on('select change', function () {
+ if ($(this).val() == '') {
+ $(this).parent().parent().css('background', 'red');
+ } else {
+ $(this).parent().parent().css('background', 'unset');
+ }
+ });
+});
+
+function newPDFTemplate() {
+ var n = prompt('Name des neuen Templates:', '');
+ if (!n) return;
+
+ var text = $("#editor .ql-editor").html();
+ var subject = $('input[name=subject]').val();
+ var date = $('input[name=date]').val();
+
+ $.ajaxSetup({async:false});
+ var rv = 0;
+ $.post('/db/main/addtemplate.php', {
+ 'name': n,
+ 'text': text,
+ 'subject': subject,
+ 'date': date,
+ 'pdf': true
+ }, function (rv) { tid = rv; });
+
+ localStorage.setItem('pdf_template', tid);
+ alert(unescape('Neues Template ID ' + tid + ' mit den aktuellen Werten wurde erzeugt.\r\n\r\nDie Seite lädt nun neu.'));
+ location.reload();
+}
+
+function renamePDFTemplate() {
+ var id = $('#pdf_template :selected').attr('data-id');
+ var on = $('#pdf_template :selected').attr('data-name');
+ var n = prompt('Name des Templates:', on);
+ if (!n) return;
+
+ $.ajaxSetup({async:false});
+ $.post('/db/main/rentemplate.php', {
+ 'id': id,
+ 'name': n,
+ 'pdf': true
+ });
+
+ $('#pdf_template :selected').attr('data-name', n);
+ $('#pdf_template :selected').text(n + ' (ID ' + id + ')');
+}
+
+function delPDFTemplate() {
+ var id = $('#pdf_template').val();
+ var v = $('#pdf_template :selected').text();
+ if (!v) return;
+
+ if (confirm(unescape('Wirklich Template\n "' + v + '"\nunwiderruflich verwerfen?'))) {
+ $.ajaxSetup({async:false});
+ $.post('/db/main/deltemplate.php', {
+ 'id': id,
+ 'pdf': true
+ });
+ $('#pdf_template :selected').remove();
+ $('#pdf_template').css('background', 'red');
+ }
+}
+
+function loadPDFTemplate() {
+ console.log($('#pdf_template :selected').attr('data-text').replace(/&quot;/g, '"'));
+ $("#editor .ql-editor").html($('#pdf_template :selected').attr('data-text'));
+
+ $('input[name=subject]').val($('#pdf_template :selected').attr('data-subject'));
+ $('input[name=date]').val($('#pdf_template :selected').attr('data-date'));
+ $('select[name=fn]').val($('#pdf_template :selected').attr('data-fn'));
+
+ setTimeout(function () {
+ $('#pdf_template').css('background', '#cdf5cd');
+ }, 200);
+}
+
+function savePDFTemplate() {
+ var text = $("#editor .ql-editor").html();
+ var subject = $('input[name=subject').val();
+ var date = $('input[name=date]').val();
+ var fn = $('select[name=fn]').val();
+ var id = $('#pdf_template').val();
+
+ $('#pdf_template :selected').attr('data-text', text);
+ $('#pdf_template :selected').attr('data-subject', subject);
+ $('#pdf_template :selected').attr('data-date', date);
+ $('#pdf_template :selected').attr('data-fn', fn);
+ $('#pdf_template :selected').text($('#pdf_template :selected').attr('data-name') + ' (ID ' + $('#pdf_template :selected').attr('data-id') + ')');
+
+ $.ajaxSetup({async:false});
+ $.post('/db/main/updtemplate.php', {
+ 'id': id,
+ 'text': text,
+ 'subject': subject,
+ 'date': date,
+ 'fn': fn,
+ 'pdf': true
+ });
+
+ setTimeout(function () {
+ $('#pdf_template').css('background', '#cdf5cd');
+ }, 200);
+}
+
+<? minEnd(); ?>
+</script>
+
+<?
+$sql = $_POST['sql'];
+$post_sql = $_POST['postsql'];
+$payload_sql = $_POST['payload_sql'];
+$payload_sql_order = $_POST['payload_sql_order'];
+$independent_payload_sql = $_POST['independent_payload_sql'];
+$independent_payload_sql_order = $_POST['independent_payload_sql_order'];
+require_once __DIR__ . "/templater_common.php";
+?>
+
+<script>
+<? minStart(); ?>
+
+function getOne() {
+ if ($('select[name=fn]').val() == "") {
+ alert(unescape('Bitte Auswahl bei Dokument (Dateiname) treffen!'));
+ return;
+ }
+
+ var html = $('#preview').html();
+ $('input[name=html]').val(html);
+ $('#getpdf').submit();
+}
+
+function sendOne(skip) {
+ if ($('select[name=fn]').val() == "") {
+ alert(unescape('Bitte Auswahl bei Dokument (Dateiname) treffen!'));
+ return;
+ }
+
+ if (typeof skip !== 'boolean')
+ skip = false;
+
+ if (typeof pivot.__done !== 'undefined') {
+ if (!confirm(unescape('Diese Adressatenkombination wurde schon generiert. Wiederholen und Überschreiben?')))
+ return;
+ }
+
+ var subject = $('input[name=subject]').val();
+ var fn = $('select[name=fn]').val();
+ var date = $('input[name=date]').val();
+ var organisation = $('input[name=organisation]').val();
+ var anrede = $('input[name=anrede]').val();
+ var anrede_briefkopf = $('input[name=anrede_briefkopf]').val();
+ var titel = $('input[name=titel]').val();
+ var vorname = $('input[name=vorname]').val();
+ var nachname = $('input[name=nachname]').val();
+ var strasse = $('input[name=strasse]').val();
+ var adresszusatz = $('input[name=adresszusatz]').val();
+ var plz = $('input[name=plz]').val();
+ var ort = $('input[name=ort]').val();
+ var template = $('input[name=template]').val();
+ var uid = $('input[name=uid]').val();
+ var ddata = $('input[name=data]').val();
+ var html = $('#preview').html();
+
+ var noerror = false;
+ var response = "";
+ $.ajaxSetup({async:false});
+ $.ajax({
+ type: 'POST',
+ url: '/db/main/dopdf.php?save',
+ data: {
+ 'subject': subject,
+ 'fn': fn,
+ 'date': date,
+ 'organisation': organisation,
+ 'anrede': anrede,
+ 'anrede_briefkopf': anrede_briefkopf,
+ 'titel': titel,
+ 'vorname': vorname,
+ 'nachname': nachname,
+ 'strasse': strasse,
+ 'adresszusatz': adresszusatz,
+ 'plz': plz,
+ 'ort': ort,
+ 'template': template,
+ 'uid': uid,
+ 'data': ddata,
+ 'html': html
+ },
+ success: function (d, s, x) {
+ if (d == 1) {
+ noerror = true;
+ response = x.responseText;
+ }
+ }
+ });
+
+ if (noerror && response == '1') {
+ pivot.__done = true;
+ delete pivot.__failed;
+
+ redoRows($("tr[data-id='" + pivot.uid + "']")[0], 0, 0, 0, pivot.index);
+ redoRows($("tr[data-id='" + pivot.UID + "']")[0], 0, 0, 0, pivot.index);
+ redoRows($("tr[data-id='" + pivot.ruid + "']")[0], 0, 0, 0, pivot.index);
+ redoRows($("tr[data-id='" + pivot.RUID + "']")[0], 0, 0, 0, pivot.index);
+ redoRows($("tr[data-id='" + pivot.id + "']")[0], 0, 0, 0, pivot.index);
+ redoRows($("tr[data-id='" + pivot.ID + "']")[0], 0, 0, 0, pivot.index);
+ redoRows($("tr[data-id='" + pivot.genuid + "']")[0], 0, 0, 0, pivot.index);
+
+ var stl = "";
+ if (organisation) stl += organisation + ' / ';
+ stl += vorname + ' ' + nachname;
+
+ if (!skip) alert('PDF erfolgreich fuer ' + stl + ' erstellt.');
+
+ $('#pdf_status').html('PDF erfolgreich: ' + stl);
+ return true;
+ }
+
+ pivot.__failed = true;
+ delete pivot.__done;
+
+ redoRows($("tr[data-id='" + pivot.uid + "']")[0], 0, 0, 0, pivot.index);
+ redoRows($("tr[data-id='" + pivot.UID + "']")[0], 0, 0, 0, pivot.index);
+ redoRows($("tr[data-id='" + pivot.id + "']")[0], 0, 0, 0, pivot.index);
+ redoRows($("tr[data-id='" + pivot.ID + "']")[0], 0, 0, 0, pivot.index);
+ redoRows($("tr[data-id='" + pivot.ruid + "']")[0], 0, 0, 0, pivot.index);
+ redoRows($("tr[data-id='" + pivot.RUID + "']")[0], 0, 0, 0, pivot.index);
+ redoRows($("tr[data-id='" + pivot.genuid + "']")[0], 0, 0, 0, pivot.index);
+
+ var stl = "";
+ if (organisation) stl += organisation + ' / ';
+ stl += vorname + ' ' + nachname;
+
+ alert('Fehler beim Generieren fuer ' + stl + ': ' + response);
+ $('#pdf_status').html('Fehler: ' + stl);
+ return false;
+}
+
+function waitASec(time) {
+ $.ajaxSetup({async:false});
+ $.post('/db/main/delay.php', {
+ time: time
+ });
+}
+
+function sendAllDone() {
+ $('#pdf_modal').hide();
+ window.scrollTo(0, 0);
+ alert(unescape('Fertig. Erfolg für ' + count_s + ' Adressaten.\nFehlgeschlagen bei ' + count_f + '.'));
+ $('#pdf_status').html('Erfolg: ' + count_s + ', Fehler: ' + count_f);
+}
+
+function sendAllLoop() {
+ if (typeof window.emergencyOff !== 'undefined') {
+ sendAllDone();
+ delete window.emergencyOff;
+ return;
+ }
+
+ if (typeof pivot.__done === 'undefined') {
+ $('#pdf_status').html('Generiere fuer: ' + pivot['Vorname'] + ' ' + pivot['Nachname']);
+
+ if (sendOne(true)) count_s++;
+ else count_f++;
+
+ pivot_iter++;
+ if (pivot_iter < data.length) {
+ pivot = data[pivot_iter];
+ bP();
+ setTimeout(sendAllLoop, 100);
+ return;
+ }
+
+ setTimeout(sendAllDone, 100);
+ return;
+ }
+
+ pivot_iter++;
+ if (pivot_iter < data.length) {
+ pivot = data[pivot_iter];
+ bP();
+ setTimeout(sendAllLoop, 10);
+ return;
+ }
+
+ setTimeout(sendAllDone, 100);
+ return;
+}
+
+function sendAll() {
+ if ($('select[name=fn]').val() == "") {
+ alert(unescape('Bitte Auswahl bei Dokument (Dateiname) treffen!'));
+ return;
+ }
+
+ if (!confirm(unescape('Wirklich fuer ' + $.grep(data, function (e) { return (typeof e.__done === 'undefined'); }).length + ' Adressaten generieren?')))
+ return;
+
+ count_s = 0;
+ count_f = 0;
+ pivot = data[0];
+ pivot_iter = 0;
+ bP();
+
+ window.scrollTo(0, 0);
+ $('#pdf_modal').show();
+ setTimeout(sendAllLoop, 100);
+}
+
+function abortAll() {
+ window.emergencyOff = true;
+}
+
+<? minEnd(); ?>
+</script>
+
+<?
+include_once __DIR__ . "/footer.php";
+?>
diff --git a/pers.php b/pers.php
new file mode 100644
index 0000000..0b36f46
--- /dev/null
+++ b/pers.php
@@ -0,0 +1,177 @@
+<?php
+/*
+ * 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.
+ */
+?>
+<?
+/* vim: set ts=4 sw=4 et : */
+
+require_once __DIR__ . "/check_auth.php";
+require_once __DIR__ . "/../includes/common.php";
+
+session_write_close();
+
+require_once __DIR__ . "/lookup.php";
+require_once __DIR__ . "/lookup_more.php";
+
+$persID = $_GET['id'];
+include_once __DIR__ . "/fotolink.php";
+
+(function () use ($mysqli, $_title, $persID, $fotolink, &$email) {
+ $sql = "
+ SELECT Personen.*, DATE(Geburtsdatum) AS Geburtsdatum, Personen_Prefs.salutation, IF(UNIX_TIMESTAMP(NOW()) - UNIX_TIMESTAMP(appprofiles.lastnot) < (60*60*24), 1, 0) AS AppNotifications, appprofiles.id AS AppProfileID, SUM(SpendenTOT.Betrag) AS `Gesamtsumme`, (SELECT SUM(Spenden.Betrag) FROM Spenden WHERE Spenden.Person = ? AND Spenden.SuperID IS NULL AND DATE_SUB(CURDATE(), INTERVAL 365 DAY) <= Spenden.Geldeingang) AS `Summe letzte 365 Tage`
+ FROM Personen
+ LEFT JOIN Personen_Prefs ON Personen.ID = Personen_Prefs.persid AND Personen_Prefs.userid = ?
+ LEFT JOIN appprofiles ON Personen.ID = appprofiles.persid
+ LEFT JOIN Spenden AS SpendenTOT ON Personen.ID = SpendenTOT.Person AND SpendenTOT.SuperID IS NULL
+ WHERE Personen.ID=?
+ ";
+ $stmt = $mysqli->prepare($sql);
+ $stmt->bind_param('iii', $persID, $_SESSION['auth_userid'], $persID);
+ $stmt->execute();
+ $r = $stmt->get_result();
+ $l = $r->fetch_object();
+ $l->Foto = $fotolink;
+ $email = $l->Email;
+
+ $_title = '<span class="pii">' . $l->Nachname . ', ' . $l->Vorname . '</span> (ID ' . $persID . ')';
+ include_once __DIR__ . "/header.php";
+
+ $id = "persview";
+ $pdf_sql = $email_sql = "SELECT *, DATE(Geburtsdatum), CONCAT('', Personen.ID, 'person') AS uid FROM Personen WHERE Personen.ID IN ";
+ include __DIR__ . '/autoemail.php';
+ include __DIR__ . '/autopdf.php';
+?>
+
+<h1 style="display: inline-block;"><?=$_title?></h1>
+&nbsp;<button class="small" onclick="return doEmail_persview(<?=$persID?>);"><i class="fas fa-envelope"></i> E-Mail senden</button>
+&nbsp;<button class="small" onclick="return doPDF_persview(<?=$persID?>);"><i class="fas fa-file-pdf"></i> PDF generieren</button>
+&nbsp;<button class="small" onclick="showNewStipDialog();"><i class="fas fa-money-check-alt"></i> Stipendium anlegen</button>
+&nbsp;<button class="small" onclick="showNewNoteDialog({'persid': <?=$persID?>});"><i class="fas fa-sticky-note"></i> Notiz anlegen</button>
+&nbsp;<button class="small" onclick="showNewOrgaPersonDialog(<?=$persID?>);"><i class="fas fa-project-diagram"></i></sup> zu Orga hinzuf&uuml;gen</button>
+&nbsp;<button class="small" onclick="showAddEventDialog(<?=$persID?>);"><i class="fas fa-glass-cheers"></i></sup> zu Event hinzuf&uuml;gen</button>
+&nbsp;<button class="small" style="background-color: red;" onclick="location.href='/db/main/delperson.php?id=<?=$persID?>';"><i class="fas fa-trash-alt"></i></sup> Datensatz löschen</button>
+
+<div style="display: none;" id="new_stip" title="Neues Stipendium">
+ <form action="/db/main/newstip.php" method="post">
+ <label for="persid">PersonenID*</label><br />
+ <input type="text" readonly name="persid" value="<?=$persID?>"><br />
+ <label for="jahr">Jahr*</label><br />
+ <input type="text" name="jahr"><br />
+ <label for="foerderart">F&ouml;rderart</label><br />
+ <select name="foerderart">
+ <option></option>
+<?
+ (function () {
+ global $foerderarten;
+ foreach ($foerderarten as $k => $v) {
+?>
+ <option value="<?=$k?>"><?=$v?></option>
+<?
+ }
+ })();
+?>
+ </select>
+
+ <br /><br />
+ <input type="submit" value="Anlegen">
+ </form>
+</div>
+
+<script>
+<? minStart(); ?>
+
+function showNewStipDialog() {
+ $('#new_stip').dialog({
+ minWidth: 330,
+ minHeight: 240,
+ });
+ setTimeout(function () {
+ $('#new_stip input[name=jahr]').focus().select();
+ }, 100);
+}
+
+<? minEnd(); ?>
+</script>
+
+<?
+ $id = "pers";
+ $entrytable = "Personen";
+ $def = [
+ "Stammdaten",
+ [11, '=2Geschlecht', '=3Anrede', '=3anrede_briefkopf~Anrede Briefkopf', '_^w~w <i class="fas fa-venus"></i>', '_^m~m <i class="fas fa-mars"></i>', '_^d~div'],
+ ['informale Ansprache', 'Ansprache', 'Titel', 'salutation|salutations~Individ. autom. Ansprache <small>(' . $_SESSION['auth_user'] . ')</small>'],
+ ['Vorname', 'Nachname', 'Geburtsname', 'user'],
+ ['!Foto', 'Geburtsdatum#isodate', 'Geburtsort', 'Staatsangehörigkeit|laenderByID'],
+ "Kontaktdaten",
+ ['Straße', 'Adresszusatz'],
+ ['PLZ', 'Ort', 'Land|laenderByID'],
+ ['Email#email', 'Email-Privat#email', 'Email-Geschäftlich#email'],
+ ['Telefon', 'Handy', 'Fax', 'Homepage'],
+ "Spenderinformationen",
+ ['+Liste Privatspender', '+Ewige Liste Privatspender', '_Gesamtsumme', '_Summe letzte 365 Tage'],
+ "Sonstiges",
+ [6, '_Newsletter|threeWay~Anmeldung Newsletter', '+wuenscht_keine_Emails~wünscht keine Emails', '+stellenangebote~Erhalt Stellenangebote', '+ideellesfp~Ideelles Förderprogramm', '+infos~Erhalt Infos', '+community~Studienfonds Community']
+ ];
+
+ if (in_array($persID, [1, 2, 3])) {
+ $def[] = ['*signature~Signatur'];
+ }
+
+ $bottom = <<<EOD
+ <script>
+ $('#persForm button.btn_w').click(function () {
+ $('#persForm input[name=Geschlecht]').val('weiblich').trigger('change');
+ $('#persForm input[name=Anrede]').val('Frau').trigger('change');
+ $('#persForm input[name=anrede_briefkopf]').val('Frau').trigger('change');
+ $('#persForm input[name="informale Ansprache"]').val('Liebe').trigger('change');
+ $('#persForm input[name=Ansprache]').val('Sehr geehrte Frau').trigger('change');
+ return false;
+ });
+
+ $('#persForm button.btn_m').click(function () {
+ $('#persForm input[name=Geschlecht]').val('männlich').trigger('change');
+ $('#persForm input[name=Anrede]').val('Herr').trigger('change');
+ $('#persForm input[name=anrede_briefkopf]').val('Herrn').trigger('change');
+ $('#persForm input[name="informale Ansprache"]').val('Lieber').trigger('change');
+ $('#persForm input[name=Ansprache]').val('Sehr geehrter Herr').trigger('change');
+ return false;
+ });
+
+ $('#persForm button.btn_d').click(function () {
+ $('#persForm input[name=Geschlecht]').val('divers').trigger('change');
+ $('#persForm input[name=Anrede]').val('').trigger('change');
+ $('#persForm input[name=anrede_briefkopf]').val('').trigger('change');
+ $('#persForm input[name="informale Ansprache"]').val('Hallo').trigger('change');
+ $('#persForm input[name=Ansprache]').val('Guten Tag').trigger('change');
+ return false;
+ });
+ </script>
+EOD;
+
+ include __DIR__ . '/autoform.php';
+ $stmt->reset();
+})();
+
+(function () use ($persID) {
+ $globstring = "{*-" . $persID . "person*," . $persID . "person*}_";
+ require_once __DIR__ . "/doc.php";
+})();
+
+include_once __DIR__ . '/notesXpers.php';
+include_once __DIR__ . '/emailsXpers.php';
+include_once __DIR__ . '/stipsXpers.php';
+include_once __DIR__ . '/eventsXpers.php';
+include_once __DIR__ . '/donationsXpers.php';
+include_once __DIR__ . '/orgaXpers.php';
+
+require_once __DIR__ . "/jumper.php";
+require_once __DIR__ . "/footer.php";
diff --git a/persXorga.php b/persXorga.php
new file mode 100644
index 0000000..367e149
--- /dev/null
+++ b/persXorga.php
@@ -0,0 +1,130 @@
+<?php
+/*
+ * 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.
+ */
+?>
+<?php
+/* vim: set ts=4 sw=4 et : */
+
+(function () use ($orgaID) {
+ if (!assureInt($orgaID)) return;
+
+ $id = "persXorga3";
+ $title = "Personen zur Organisation";
+ $sql = "
+SELECT
+ Personen_Organisationen.ID PersOrgaID, Personen.ID AS PersID, Personen.Nachname, Personen.Vorname, Personen.Email,
+ Funktion, Abteilung,
+
+ apstip, apsf, apop, apstr, apfi,
+ elsv, elfa, elsft, elosv,
+ ernl, erwk, erwm
+
+FROM Personen_Organisationen
+LEFT JOIN Personen ON Personen_Organisationen.Person = Personen.ID
+WHERE Personen_Organisationen.Organisation=" . $orgaID;
+
+ $getthdef = true;
+ $order = '[[2, "asc"], [3, "asc"]]';
+ $entrytable = 'Personen_Organisationen';
+ $idcell = "PersOrgaID";
+ $idcellreal = "ID";
+ $checkboxes = true;
+
+ $editable = [
+ 'Funktion',
+ 'Abteilung',
+
+ 'apstip',
+ 'apsf',
+ 'apop',
+ 'apstr',
+ 'apfi',
+
+ 'elsv',
+ 'elfa',
+ 'elsft',
+ 'elosv',
+
+ 'ernl',
+ 'erwk',
+ 'erwm',
+ ];
+ $types = [
+ 'Ansprechpartner' => 'checkbox',
+ 'apstip' => 'checkbox',
+ 'apsf' => 'checkbox',
+ 'apop' => 'checkbox',
+ 'apstr' => 'checkbox',
+ 'apfi' => 'checkbox',
+
+ 'elsv' => 'checkbox',
+ 'elfa' => 'checkbox',
+ 'elsft' => 'checkbox',
+ 'elosv' => 'checkbox',
+
+ 'ernl' => 'checkbox',
+ 'erwk' => 'checkbox',
+ 'erwm' => 'checkbox',
+ ];
+
+ $bottom = <<<EOD
+<div class="admin_actionsbelow">
+ Ausgewählte
+ <button class="medium" style="background: darkorange;" onclick="return delPO();"><i class="fas fa-trash-alt"></i> löschen</button>
+</div>
+
+<button class="medium" onclick="showNewOrgaPersonDialog('', $orgaID, window.location.href);"><i class="fas fa-user"></i><sup><i class="fas fa-project-diagram"></i><i class="fas fa-plus"></i></sup> Person bei Organisation</button>
+
+<script>
+function delPO() {
+ $.ajaxSetup({async:false});
+ $.ajax({
+ type: 'POST',
+ url: '/db/main/delorgaperson.php',
+ data: {
+ 'ids': getIDs_$id()
+ }
+ });
+ $.ajaxSetup({async:true});
+ location.reload();
+ return false;
+}
+</script>
+EOD;
+
+ $nospinner = true;
+ $insert = pathinfo(basename(__FILE__), PATHINFO_FILENAME);
+ include __DIR__ . '/autotable.php';
+
+ $pdf_sql = $email_sql = "
+ SELECT
+ CONCAT('', Personen_Organisationen.Organisation, 'orga', '-', Personen.ID, 'person') AS uid,
+ Personen.Geschlecht,
+ Personen.`informale Ansprache`,
+ Personen.Ansprache,
+ Personen.Anrede,
+ Personen.Titel,
+ Personen.Nachname,
+ Personen.Vorname,
+ Personen.Email,
+ Personen.Telefon,
+ Personen.Handy,
+ Organisationen.*
+ FROM Personen_Organisationen
+ INNER JOIN Personen ON Personen_Organisationen.Person = Personen.ID
+ LEFT JOIN Organisationen ON Personen_Organisationen.Organisation = Organisationen.ID
+ WHERE Personen_Organisationen.ID IN ";
+ include __DIR__ . '/autoactions.php';
+ include __DIR__ . '/autoemail.php';
+ include __DIR__ . '/autopdf.php';
+ include __DIR__ . '/autoevent.php';
+})();
diff --git a/persons.php b/persons.php
new file mode 100644
index 0000000..8adba95
--- /dev/null
+++ b/persons.php
@@ -0,0 +1,50 @@
+<?php
+/*
+ * 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.
+ */
+?>
+<?php
+/* vim: set ts=4 sw=4 et : */
+
+$_title = "Alle Personen";
+
+require_once __DIR__ . "/check_auth.php";
+include_once __DIR__ . "/header.php";
+require_once __DIR__ . "/../includes/common.php";
+require_once __DIR__ . "/lookup.php";
+
+doTitle();
+
+(function () use ($mysqli) {
+ $sql = "
+SELECT ID AS PersID, Nachname, Vorname, Geschlecht, Ort, Email, Telefon, Handy
+FROM Personen
+";
+ $stmt = $mysqli->prepare($sql);
+ $stmt->execute();
+ $r = $stmt->get_result();
+ $id = "persons";
+ $order = '[[2, "asc"], [3, "asc"]]';
+ $checkboxes = true;
+ $idcell = "PersID";
+ $nospinner = true;
+ $ajax = true;
+ include __DIR__ . '/autotable.php';
+ $stmt->reset();
+
+ $pdf_sql = $email_sql = "SELECT Personen.*, Personen.ID AS PersID FROM Personen WHERE ID IN ";
+ include __DIR__ . '/autoactions.php';
+ include __DIR__ . '/autoemail.php';
+ include __DIR__ . '/autopdf.php';
+ include __DIR__ . '/autoevent.php';
+})();
+
+include_once __DIR__ . "/footer.php";
diff --git a/php.scm b/php.scm
new file mode 100644
index 0000000..4e4b82d
--- /dev/null
+++ b/php.scm
@@ -0,0 +1,66 @@
+(define prepare-destatis-countries
+ '(!php "if ($stmt) $stmt->reset(); $sql = \"SELECT ID, Code, Name FROM Länder ORDER BY Prio DESC, Name ASC;\"; $stmt = $mysqli->prepare($sql); $stmt->bind_result($id, $code, $name); $stmt->execute();"))
+
+(define prepare-iso3166-countries-de
+ '(!php "if ($stmt) $stmt->reset(); $sql = \"SELECT code, de FROM iso3166 ORDER BY de;\"; $stmt = $mysqli->prepare($sql); $stmt->bind_result($code, $name); $stmt->execute();"))
+
+(define prepare-iso3166-countries-en
+ '(!php "if ($stmt) $stmt->reset(); $sql = \"SELECT code, en FROM iso3166 ORDER BY en;\"; $stmt = $mysqli->prepare($sql); $stmt->bind_result($code, $name); $stmt->execute();"))
+
+(define prepare-iso3166-countries
+ (decide-by-lang prepare-iso3166-countries-de prepare-iso3166-countries-en))
+
+(define (iter-countries-php var)
+ (string-append "while ($stmt->fetch()) { ?> <option value=\"<?=$code?>\" ng-selected=\"d." var " == '<?=$code?>'\"><?=$name?></option> <? }"))
+
+(define (prepare-u-location u)
+ `(!php
+ ,(string-append
+ "if ($stmt) $stmt->reset();$sql = \"SELECT `Standort` FROM Abschlusskombis WHERE (HochschuleText='" u "' "
+ "OR Hochschule='" u "') AND active=1 GROUP BY `Standort` ORDER BY `Standort`;\";"
+ "$stmt = $mysqli->prepare($sql);$stmt->bind_result($location);$stmt->execute();")))
+
+(define (iter-u-location var)
+ (string-append "while ($stmt->fetch()) { ?> <option value=\"<?=$location?>\" ng-selected=\"d." var " == '<?=$location?>'\"><?=$location?></option> <? }"))
+
+(define prepare-course-of-study '(!php "if ($stmt) $stmt->reset();$sql = \"SELECT `Text Studienfach` FROM Abschlusskombis WHERE (HochschuleText=? OR HochschuleText=?) AND active=1 GROUP BY `Text Studienfach` ORDER BY `Text Studienfach`;\";$stmt = $mysqli->prepare($sql);$stmt->bind_param('ss', $hsname, $hs);$stmt->bind_result($sname);$stmt->execute();"))
+
+(define (iter-course-of-study-php var)
+ (string-append "while ($stmt->fetch()) { ?> <option value=\"<?=$sname?>\" ng-selected=\"d." var " == '<?=$sname?>'\"><?=$sname?></option> <?php }"))
+
+(define get-organization-name-php '(!php "if ($stmt) $stmt->reset(); $sql = \"SELECT Organisationen.Name, KanonischerName FROM Hochschulen INNER JOIN Organisationen ON Hochschulen.Organisation = Organisationen.ID WHERE Code=?;\"; $stmt = $mysqli->prepare($sql); $stmt->bind_param('s', $hs); $stmt->bind_result($hsfull, $hsname); $stmt->execute(); $stmt->fetch(); echo $hsfull;"))
+
+(define prepare-fakultaet '(!php "if ($stmt) $stmt->reset(); $sql = \"SELECT Fakultät FROM Abschlusskombis WHERE (HochschuleText=? OR HochschuleText=?) AND TRIM(Fakultät) <> '' AND active=1 GROUP BY Fakultät;\"; $stmt = $mysqli->prepare($sql); $stmt->bind_param('ss', $hsname, $hs); $stmt->bind_result($fak); $stmt->execute();"))
+
+(define iter-fakultaet "while ($stmt->fetch()) { ?> <option value=\"<?php echo $fak; ?>\" ng-selected=\"d.fakultaet == '<?php echo $fak; ?>'\"><?php echo $fak; ?></option><?php }")
+
+(define prepare-studiumart '(!php "if ($stmt) $stmt->reset(); $sql = \"SELECT `Art Studium` FROM Abschlusskombis WHERE (HochschuleText=? OR HochschuleText=?) AND `Art Studium` NOT LIKE '%Kein Abschluss%' AND active=1 GROUP BY `Art Studium`;\"; $stmt = $mysqli->prepare($sql); $stmt->bind_param('ss', $hsname, $hs); $stmt->bind_result($sart); $stmt->execute();"))
+
+(define iter-studiumart "while ($stmt->fetch()) { ?> <option value=\"<?=$sart?>\" ng-selected=\"d.studium_art == '<?=$sart?>'\"><?=$sart?></option><?php }")
+
+(define prepare-abschlusskombis '(!php "if ($stmt) $stmt->reset();$sql = \"SELECT `Text Abschluss`, `Art Studium` FROM Abschlusskombis WHERE (HochschuleText=? OR HochschuleText=?) AND active=1 GROUP BY `Text Abschluss`, `Art Studium`;\";$stmt = $mysqli->prepare($sql);$stmt->bind_param('ss', $hsname, $hs);$stmt->bind_result($sabschluss, $sart);$stmt->execute();"))
+
+(define iter-abschlusskombis "while ($stmt->fetch()) { ?><option data-art=\"<?=$sart?>\" value=\"<?=$sabschluss?>\" ng-selected=\"d.studium_abschluss == '<?=$sabschluss?>'\"><?=$sabschluss?></option><?php }")
+
+(define prepare-studiengang '(!php "if ($stmt) $stmt->reset();$sql = \"SELECT ID, `Text Studienfach`, `Art Studium`, `Text Abschluss` FROM Abschlusskombis WHERE (HochschuleText=? OR HochschuleText=?) AND active=1 ORDER BY `Text Studienfach`;\";$stmt = $mysqli->prepare($sql);$stmt->bind_param('ss', $hsname, $hs);$stmt->bind_result($id, $sname, $sart, $sabschluss);$stmt->execute();"))
+
+(define iter-studiengang "while ($stmt->fetch()) { ?><option value=\"<?=$id?>\" data-art=\"<?=$sart?>\" data-abschluss=\"<?=$sabschluss?>\" ng-selected=\"d.studiengang == '<?=$id?>'\"><?=$sname?></option><?php }")
+
+(define prepare-abschlussarten '(!php "if ($stmt) $stmt->reset();$sql = \"SELECT ID, Code, Name FROM Abschlussarten;\";$stmt = $mysqli->prepare($sql);$stmt->bind_result($id, $code, $name);$stmt->execute();"))
+
+(define iter-abschlussarten "while ($stmt->fetch()) { ?><option value=\"<?=$id?>\" ng-selected=\"s.zs_art == '<?=$id?>'\"><?=$name?></option><?php }")
+
+(define prepare-abschluss '(!php "if ($stmt) $stmt->reset();$sql = \"SELECT ID, Name FROM AbschlüsseEinfach;\";$stmt = $mysqli->prepare($sql);$stmt->bind_result($id, $name);$stmt->execute();"))
+
+(define iter-abschluss "while ($stmt->fetch()) { ?> <option value=\"<?=$id?>\" ng-selected=\"s.zs_abschluss == '<?=$id?>'\"><?=$name?></option><?php } ")
+
+(define* (iter-multicontainer-options-php startvalue maxvalue incvalue suffix variableprefix variable #:key (calcyear "1") . opt)
+ (string-append "for ($i = " startvalue "; $i <=" maxvalue "; $i+=" incvalue ") { ?> <option value=\"<?=$i?>\" ng-selected=\"" variableprefix "." variable " =='<?=$i?>'\"><?=$i /" calcyear ";?> " suffix "</option> <? }" ))
+
+(define iter-s5-engagement-cur-start "for ($j = date('Y'); $j >= 1970; $j--) { for ($i = 12; $i > 0; $i--) { ?> <option value=\"<?php echo $j;?>/<?php echo str_pad($i, 2, '0', STR_PAD_LEFT);?>\" ng-selected=\"b.start == '<?php echo $j;?>/<?php echo str_pad($i, 2, '0', STR_PAD_LEFT);?>'\"><?php echo str_pad($i, 2, '0', STR_PAD_LEFT);?>/<?php echo $j;?></option> <? }}")
+
+(define iter-s5-engagement-umfang "for ($i = 1; $i < 51; $i++) { ?> <option value=\"<?=$i?>\" ng-selected=\"b.umfang == '<?=$i?>'\"><?=$i?></option> <? }")
+
+(define iter-s5-engagement-past-start "for ($j = date('Y'); $j >= 1970; $j--) { for ($i = date('Y') == $j ? 6 : 12; $i > 0; $i--) {?> <option value=\"<?php echo $j;?>/<?php echo str_pad($i, 2, '0', STR_PAD_LEFT);?>\" ng-selected=\"b.start == '<?php echo $j;?>/<?php echo str_pad($i, 2, '0', STR_PAD_LEFT);?>'\"><?php echo str_pad($i, 2, '0', STR_PAD_LEFT);?>/<?php echo $j;?></option><? }}")
+
+(define iter-s5-engagement-past-end "for ($j = date('Y'); $j >= 1970; $j--) { for ($i = date('Y') == $j ? 6 : 12; $i > 0; $i--) {?><option value=\"<?php echo $j;?>/<?php echo str_pad($i, 2, '0', STR_PAD_LEFT);?>\" ng-selected=\"b.ende == '<?php echo $j;?>/<?php echo str_pad($i, 2, '0', STR_PAD_LEFT);?>'\"><?php echo str_pad($i, 2, '0', STR_PAD_LEFT);?>/<?php echo $j;?></option><? }}")
diff --git a/robots.txt b/robots.txt
new file mode 100644
index 0000000..1f53798
--- /dev/null
+++ b/robots.txt
@@ -0,0 +1,2 @@
+User-agent: *
+Disallow: /
diff --git a/searchorga.php b/searchorga.php
new file mode 100644
index 0000000..6ca70db
--- /dev/null
+++ b/searchorga.php
@@ -0,0 +1,64 @@
+<?php
+/*
+ * 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.
+ */
+?>
+<?php
+/* vim: set ts=4 sw=4 et : */
+
+$_search = $_GET['search'];
+$_title = "Organisationssuche: " . $_search;
+
+require_once __DIR__ . "/check_auth.php";
+require_once __DIR__ . "/../includes/common.php";
+include_once __DIR__ . "/header.php";
+?>
+
+<h1><?=$_title?></h1>
+
+<p>Es wird gesucht in: Name, ID.</p>
+
+<?
+(function () use ($mysqli, $_search) {
+ $sql = "
+SELECT
+ Organisationen.ID AS OrgaID, Organisationen.Name, Förderer.ID AS FoerdID, Superorga.ID AS SuperOrgaID, Superorga.Name AS Superorganisation
+FROM Organisationen
+LEFT JOIN Förderer ON Organisationen.ID = Förderer.Organisation
+LEFT JOIN Organisationen AS Superorga ON Organisationen.Superorganisation = Superorga.ID
+WHERE
+ Organisationen.Name LIKE ?
+ OR Organisationen.ID = ?
+";
+ $s = '%' . $_search . '%';
+ $stmt = $mysqli->prepare($sql);
+ $stmt->bind_param('si', $s, $_search);
+ $stmt->execute();
+ $r = $stmt->get_result();
+ if ($r->num_rows === 1 && !isset($_GET['action'])) {
+ $l = $r->fetch_object();
+ $stmt->reset();
+ $mysqli->close();
+?>
+ <script>
+ window.location = '/db/orga/<?=$l->OrgaID?>';
+ </script>
+<?
+ exit(0);
+ }
+ $id = "orgasearch2";
+ $order = '[[1, "asc"]]';
+ include __DIR__ . '/autotable.php';
+ $stmt->reset();
+})();
+
+include_once __DIR__ . "/footer.php";
+?>
diff --git a/searchpatron.php b/searchpatron.php
new file mode 100644
index 0000000..f760436
--- /dev/null
+++ b/searchpatron.php
@@ -0,0 +1,67 @@
+<?php
+/*
+ * 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.
+ */
+?>
+<?php
+/* vim: set ts=4 sw=4 et : */
+
+$_search = $_GET['search'];
+$_title = "Förderersuche: " . $_search;
+
+require_once __DIR__ . "/check_auth.php";
+require_once __DIR__ . "/../includes/common.php";
+include_once __DIR__ . "/header.php";
+?>
+
+<h1><?=$_title?></h1>
+
+<p>Es wird gesucht in: Organisation, Zuständig, ID.</p>
+
+<?
+(function () use ($mysqli, $_search) {
+ $sql = "
+SELECT
+ Förderer.ID AS FoerdID, Organisationen.Name AS Organisation, Organisationen.ID AS OrgaID,
+ Personen.Nachname AS Zuständig,
+ DATE(Förderer.`Vertrag läuft aus`) AS `Vertrag läuft aus`
+FROM Förderer
+LEFT JOIN Organisationen ON Förderer.Organisation = Organisationen.ID
+LEFT JOIN Personen ON Förderer.Zuständig = Personen.ID
+WHERE
+ Organisationen.Name LIKE ?
+ OR Personen.Nachname LIKE ?
+ OR Förderer.ID = ?
+";
+ $s = '%' . $_search . '%';
+ $stmt = $mysqli->prepare($sql);
+ $stmt->bind_param('ssi', $s, $s, $_search);
+ $stmt->execute();
+ $r = $stmt->get_result();
+ if ($r->num_rows === 1 && !isset($_GET['action'])) {
+ $l = $r->fetch_object();
+ $stmt->reset();
+ $mysqli->close();
+?>
+ <script>
+ window.location = '/db/patron/<?=$l->FoerdID?>';
+ </script>
+<?
+ exit(0);
+ }
+ $id = "patronsearch2";
+ $order = '[[1, "asc"]]';
+ include __DIR__ . '/autotable.php';
+ $stmt->reset();
+})();
+
+include_once __DIR__ . "/footer.php";
+?>
diff --git a/searchperson.php b/searchperson.php
new file mode 100644
index 0000000..48b1c09
--- /dev/null
+++ b/searchperson.php
@@ -0,0 +1,100 @@
+<?php
+/*
+ * 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.
+ */
+?>
+<?
+/* vim: set ts=4 sw=4 et : */
+
+$_search = $_GET['search'];
+$_title = "Personensuche: " . $_search;
+$_subtitle = '<p>Es wird gesucht in: Vorname (X) Nachname (X) { Email, Email-Privat, Email-Geschäftlich }, Ort, Geburtsdatum, ID.</p>';
+
+require_once __DIR__ . "/check_auth.php";
+require_once __DIR__ . "/../includes/common.php";
+require_once __DIR__ . "/lookup.php";
+include_once __DIR__ . "/header.php";
+
+doTitle();
+
+(function () use ($mysqli, $_search) {
+ $sql = '
+SELECT
+ ID AS PersID, Nachname, Vorname, Geschlecht, Ort, Email, Telefon, Handy, DATE(Geburtsdatum) AS Geburtsdatum, Ansprache, Anrede, `informale Ansprache`
+FROM Personen
+';
+ $constraint = '
+WHERE
+ CONCAT_WS(", ", Nachname, Vorname, Email) LIKE ?
+ OR CONCAT_WS(", ", Nachname, Vorname, `Email-Privat`) LIKE ?
+ oR CONCAT_WS(", ", Nachname, Vorname, `Email-Geschäftlich`) LIKE ?
+ OR CONCAT_WS(" ", Vorname, Nachname) LIKE ?
+ OR Ort LIKE ?
+ OR DATE(Geburtsdatum) LIKE ?
+ OR ID = ?
+';
+ $s = '%' . $_search . '%';
+ $stmt = $mysqli->prepare($sql . $constraint);
+ $stmt->bind_param('ssssssi', $s, $s, $s, $s, $s, $s, $_search);
+ $stmt->execute();
+ $r = $stmt->get_result();
+ if ($r->num_rows === 1 && !isset($_GET['action'])) {
+ $l = $r->fetch_object();
+ $stmt->reset();
+ $mysqli->close();
+?>
+ <script>
+ window.location = '/db/person/<?=$l->PersID?>';
+ </script>
+<?
+ exit(0);
+ }
+ $id = "personsearch";
+ $order = '[[2, "asc"], [3, "asc"]]';
+ $checkboxes = true;
+ $idcell = "PersID";
+ include __DIR__ . '/autotable.php';
+ $stmt->reset();
+
+ if (!isset($_GET['action'])) {
+ $pdf_sql = $email_sql = 'SELECT Personen.ID AS PersID, Personen.* FROM Personen WHERE ID IN ';
+ include __DIR__ . '/autoactions.php';
+ include __DIR__ . '/autoemail.php';
+ include __DIR__ . '/autopdf.php';
+ include __DIR__ . '/autoevent.php';
+ }
+})();
+
+if (isset($_GET['action'])) {
+?>
+ <div style="display: none;">
+ <form id="action" action="<?=$_GET['action']?>" method="post">
+ <input id="action_ids" name="ids" type="hidden" value="">
+ </form>
+ </div>
+
+ <script>
+<? minStart(); ?>
+ function doAction() {
+ var ids = [];
+ $('#personsearch tr.selected').each(function () {
+ ids.push($(this).attr('data-id'));
+ });
+ $('#action_ids').val(ids);
+ $('#action').submit();
+ }
+<? minEnd(); ?>
+ </script>
+ <button onclick="return doAction();">Okay, los!</button>
+<?
+}
+
+include_once __DIR__ . "/footer.php";
diff --git a/searchstip.php b/searchstip.php
new file mode 100644
index 0000000..c2d4e0e
--- /dev/null
+++ b/searchstip.php
@@ -0,0 +1,20 @@
+<?php
+/*
+ * 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.
+ */
+?>
+<?php
+/* vim: set ts=4 sw=4 et : */
+
+$_constraint = "";
+$_search = $_GET['search'];
+$_title = "Stipendiensuche: " . $_search;
+include_once __DIR__ . "/searchstip_common.php";
diff --git a/sendmail.php b/sendmail.php
new file mode 100644
index 0000000..2ce5739
--- /dev/null
+++ b/sendmail.php
@@ -0,0 +1,267 @@
+<?php
+/*
+ * 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.
+ */
+?>
+<?php
+/* vim: set ts=4 sw=4 et : */
+
+require_once __DIR__ . "/check_auth.php";
+require_once __DIR__ . "/../includes/common.php";
+
+session_write_close();
+
+if (isset($_SESSION['demo']) || isset($_GET['demo']) || isset($_POST['demo'])) {
+ $prop = 97;
+
+ if (rand(0, 100) > $prop) echo "0";
+ else echo "1";
+
+ exit(0);
+}
+
+$post_to = $_POST['to'];
+if (!assureString($post_to) || strlen($post_to) < 3 || strstr($post_to, '@') === false) {
+ echo "1";
+ exit(0);
+}
+
+if (isset($_SESSION['demoself']) || isset($_GET['demoself']) || isset($_POST['demoself'])) {
+ $post_to = $_POST['from'];
+}
+
+$sql = "SELECT ID FROM Personen WHERE (TRIM(LOWER(Email)) = ? OR TRIM(LOWER(`Email-Privat`)) = ? OR TRIM(LOWER(`Email-Geschäftlich`)) = ?) AND `wuenscht_keine_Emails` LIMIT 1;";
+$post_to_clean = trim(strtolower($post_to));
+$stmt = $mysqli->prepare($sql);
+$stmt->bind_param('sss', $post_to_clean, $post_to_clean, $post_to_clean);
+$stmt->bind_result($no_email);
+$stmt->execute();
+$stmt->fetch();
+$stmt->reset();
+
+if ($no_email > 0) {
+ echo "3";
+ exit(0);
+}
+
+if (
+ stristr($_POST['from'], '@upb.de') === false
+&& stristr($_POST['from'], '@uni-paderborn.de') === false
+&& stristr($_POST['from'], '@uni-paderborn.de') === false
+&& stristr($_POST['from'], '@hochschule-rhein-waal.de') === false
+&& stristr($_POST['from'], '@hsrw.dein-stip.de') === false
+) {
+ echo "4";
+ exit(0);
+}
+
+$mid = "" . time() . "-" . base_convert(bin2hex(random_bytes(8)), 16, 36) . "@hsrw.dein-stip.de";
+$boundary = "sfowl" . md5(uniqid('', true));
+
+$h = "From: " . $_POST['from'] . "\r\n";
+$h .= "Sender: " . $_POST['from'] . "\r\n";
+$h .= "Reply-To: " . "deutschlandstipendium@hochschule-rhein-waal.de" . "\r\n";
+$h .= "Errors-To: " . "deutschlandstipendium@hochschule-rhein-waal.de" . "\r\n";
+
+if (!isset($_SESSION['demoself'])) {
+ if (strlen($_POST['cc'])> 0) {
+ $h .= "Cc: " . $_POST['cc'] . "\r\n";
+ }
+
+ if (strlen($_POST['bcc'])> 0) {
+ $h .= "Bcc: " . $_POST['bcc'] . "\r\n";
+ }
+}
+
+$h .= "Message-ID: <" . $mid . ">\r\n";
+$h .= "MIME-Version: 1.0\r\n";
+
+$full = "";
+
+$html = '<style> body { font-family: "Arial Narrow", "Arial", sans-serif; } ' . "\r\n" . ' h1, h2, h3, h4, p { font-family: "Arial Narrow", "Arial", sans-serif; } ' . "\r\n" . ' ul, ol, li, strong, b, em, u, i, s, sup, sub, span, blockquote { font-family: "Arial Narrow", "Arial", sans-serif; } ' . "\r\n" . ' .ql-size-normal { font-size: 100%; } .ql-size-small { font-size: 70%; } .ql-size-large { font-size: 130%; } .ql-size-huge { font-size: 180%; } </style>' . "\r\n" . $_POST['html'];
+if (strstr($html, '<!DOCTYPE') || strstr($html, '<html')) {
+ $html = trim($_POST['html']);
+}
+
+function strip_tags_content($text, $tags = '', $invert = FALSE) {
+ preg_match_all('/<(.+?)[\s]*\/?[\s]*>/si', trim($tags), $tags);
+ $tags = array_unique($tags[1]);
+ if(is_array($tags) AND count($tags) > 0) {
+ if($invert == FALSE) {
+ return preg_replace('@<(?!(?:'. implode('|', $tags) .')\b)(\w+)\b.*?>.*?</\1>@si', '', $text);
+ } else {
+ return preg_replace('@<('. implode('|', $tags) .')\b.*?>.*?</\1>@si', '', $text);
+ }
+ }
+ elseif($invert == FALSE) {
+ return preg_replace('@<(\w+)\b.*?>.*?</\1>@si', '', $text);
+ }
+ return $text;
+}
+
+$text = trim(strip_tags(str_replace("<br>", "\r\n", str_replace("</p>", "\r\n", str_replace("<p>", "\r\n", strip_tags(strip_tags_content($_POST['html'], '<style>', TRUE), "<p><br>"))))));
+$text = preg_replace('/\h+/', ' ', $text);
+$text = preg_replace('/(\r\n\h*\r\n)+/', "\r\n", $text);
+$text = preg_replace('/(\r\n\h*\r\n)+/', "\r\n", $text);
+$text = preg_replace('/(\n\h*\n)+/', "\n", $text);
+$text = preg_replace('/(\n\h*\n)+/', "\n", $text);
+
+$exactmatch = false;
+if (isset($_POST['exactmatch']) && $_POST['exactmatch']) $exactmatch = true;
+
+$attach = false;
+$attach_fn = "";
+$attach_data = "";
+if ($_POST['attachments'] && $_POST['attachments_fn'] && $_POST['attachments_mime']) {
+ $attach = true;
+ $attach_fn = $_POST['attachments_fn'];
+ $attach_data = [];
+ foreach ($_POST['attachments'] as $el) {
+ if ($el[0] === '~') {
+ $attach_data[] = file_get_contents("/var/www/uploads/" . substr($el, 1));
+ if (!$exactmatch && end($attach_data) === false) {
+ $dn = dirname(substr($el, 1));
+ $bn = basename(substr($el, 1));
+ $ddd = glob("/var/www/uploads/*" . substr($dn, 1, -1) . "*_/" . $bn);
+ if (is_array($ddd)) {
+ $ddd = usort($ddd, function($a, $b) { return filemtime($a) - filemtime($b); });
+ reset($attach_data);
+ array_pop($attach_data);
+ $attach_data[] = file_get_contents($ddd[0]);
+ if (end($attach_data) === false) {
+ $altalt = explode('-', $dn);
+ if ($altalt == false || (is_array($altalt) && count($altalt) < 2)) {
+ echo "2";
+ exit(1);
+ }
+
+ $altddd = glob("/var/www/uploads/*" . $altalt[0] . "-*_/" . $bn);
+ if (is_array($altddd)) {
+ $ddd = usort($altddd, function($a, $b) { return filemtime($a) - filemtime($b); });
+ reset($attach_data);
+ array_pop($attach_data);
+ $attach_data[] = file_get_contents($altddd[0]);
+ if (end($attach_data) === false) {
+ echo "2";
+ exit(1);
+ }
+ } else {
+ echo "2";
+ exit(1);
+ }
+ }
+ } else {
+ echo "2";
+ exit(1);
+ }
+ }
+ reset($attach_data);
+ } else {
+ $attach_data[] = file_get_contents("/var/www/intern/pages/storage/" . $el[0] . "/" . $el[1] . "/" . $el[2] . "/" . substr($el, 3));
+ }
+ }
+ $attach_mime = $_POST['attachments_mime'];
+}
+
+if ($attach) {
+ $h .= "Content-Type: multipart/mixed; boundary=\"full_$boundary\"\r\n";
+ $full .= "--full_$boundary\r\n";
+ $full .= "Content-Type: multipart/alternative; boundary=\"$boundary\"\r\n";
+ $full .= "\r\n\r\n";
+} else {
+ $h .= "Content-Type: multipart/alternative; boundary=\"$boundary\"\r\n";
+}
+
+$full .= "--$boundary\r\n" .
+ "Content-Type: text/plain; charset=utf-8\r\n" .
+ "Content-Transfer-Encoding: quoted-printable\r\n\r\n" .
+
+ quoted_printable_encode(
+ $text
+ ) .
+
+ "\r\n\r\n" .
+
+ "--$boundary\r\n" .
+ "Content-Type: text/html; charset=utf-8\r\n" .
+ "Content-Transfer-Encoding: quoted-printable\r\n\r\n" .
+
+ quoted_printable_encode(
+ $html
+ ) .
+
+ "\r\n\r\n" .
+ "--$boundary--";
+
+if ($attach) {
+ $prefs = ['input-charset' => 'UTF-8', 'output-charset' => 'UTF-8'];
+ for ($i = 0; $i < count($attach_fn) && $i < count($attach_data) && $i < count($attach_mime); $i++) {
+ if ($attach_fn[$i][0] === '~') $attach_fn[$i] = basename($_POST['attachments'][$i]);
+ $en_fn = iconv_mime_encode("filename", $attach_fn[$i], $prefs);
+ $en_fn = substr($en_fn, strlen('filename: '));
+
+ $full .= "\r\n\r\n--full_$boundary\r\n";
+ $full .= "Content-Type: " . $attach_mime[$i] . "; name=\"" . $en_fn . "\"\r\n";
+ $full .= "Content-Disposition: attachment; filename=\"" . $en_fn . "\"\r\n";
+ $full .= "Content-Location: CID:att" . ($i+1) . "\r\n";
+ $full .= "Content-ID: <att" . ($i+1) . ">\r\n";
+ $full .= "Content-Transfer-Encoding: base64\r\n\r\n";
+
+ $full .= chunk_split(base64_encode($attach_data[$i]), 76, "\r\n");
+ }
+
+ $full .= "\r\n\r\n";
+ $full .= "--full_$boundary--";
+}
+
+$prefs = ['input-charset' => 'UTF-8', 'output-charset' => 'UTF-8'];
+$encoded_subject = iconv_mime_encode('Subject', $_POST['subject'], $prefs);
+$encoded_subject = substr($encoded_subject, strlen('Subject: '));
+
+if (isset($_SESSION['demoself'])) {
+ $e = mail($_POST['from'], $encoded_subject,
+ $full,
+ $h,
+ "-f " . $_POST['from']
+ );
+ echo 1;
+
+ exit(0);
+}
+
+$e = mail($post_to, $encoded_subject,
+ $full,
+ $h,
+ "-f " . $_POST['from']
+);
+
+syslog(LOG_INFO, 'sendmail for ' . $post_to . ' from ' . $_POST['from'] . ', errorlevel: ' . $e);
+
+if ($e == true) {
+ $persid = $_POST['persid'];
+ $stipid = $_POST['stipid'];
+ if ($persid == 0) $persid = null;
+ if ($stipid == 0) $stipid = null;
+
+ $sql = "INSERT INTO mails (mid, uid, persid, stipid, `from`, `to`, subject, html, text, header, attached) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);";
+ $stmt = $mysqli->prepare($sql);
+ $alist = "";
+ if ($_POST['attachments'] && is_array($_POST['attachments'])) {
+ $alist = implode(',', $_POST['attachments']);
+ }
+ $stmt->bind_param('ssiisssssss', $mid, $_POST['uid'], $persid, $stipid, $_POST['from'], $post_to, $_POST['subject'], $html, $text, $h, $alist);
+ $stmt->execute();
+ $stmt->reset();
+ $mysqli->close();
+}
+
+echo $e;
+?>
diff --git a/stips.php b/stips.php
new file mode 100644
index 0000000..88f6e28
--- /dev/null
+++ b/stips.php
@@ -0,0 +1,29 @@
+<?php
+/*
+ * 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.
+ */
+?>
+<?php
+/* vim: set ts=4 sw=4 et : */
+
+$_title = 'Alle Stipendien';
+
+$miny = $maxy = 0;
+if (isset($_GET['year'])) {
+ $_title .= ' (Jahrgang ' . $_GET['year'] . ')';
+ $miny = $maxy = $_GET['year'];
+}
+$_constraint = '';
+if ($miny || $maxy) {
+ $_constraint = "WHERE Jahr >= " . $miny . " AND Jahr <= " . $maxy . " AND (Förderende > '2000-01-01 00:00:00' OR Förderende IS NULL OR Förderende = '0000-00-00 00:00:00') AND (ds.accepted=1 OR ds.accepted IS NULL)";
+}
+
+require_once __DIR__ . "/stips_common.php";
diff --git a/stipspers.php b/stipspers.php
new file mode 100644
index 0000000..e051dc7
--- /dev/null
+++ b/stipspers.php
@@ -0,0 +1,57 @@
+<?php
+/*
+ * 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.
+ */
+?>
+<?php
+/* vim: set ts=4 sw=4 et : */
+
+$_title = "Alle Stipendiatinnen und Stipendiaten (nur Person)";
+$_subtitle = "<span>Hier erscheinen alle Personen, die sich aus Stipendiatendatensätzen ergeben. <b>Es wird hier nicht zwischen Stipendiaten- und Alumni-Eigenschaft unterschieden!</b> Dafür gibt es andere Abfragen.</span>";
+require_once __DIR__ . "/check_auth.php";
+require_once __DIR__ . "/../includes/common.php";
+include_once __DIR__ . "/header.php";
+require_once __DIR__ . "/lookup.php";
+
+doTitle();
+
+(function () {
+ $id = "stipspers";
+ $sql = "
+SELECT
+ Personen.ID AS PersID,
+ Personen.Nachname, Personen.Vorname,
+ Personen.Geschlecht,
+ Personen.Ort,
+ Personen.Email,
+ Personen.Telefon,
+ Personen.Handy
+
+FROM Stipendien
+LEFT JOIN Personen ON Stipendien.Person = Personen.ID
+WHERE (Stipendien.Förderende > '2000-01-01 00:00:00' OR Stipendien.Förderende IS NULL OR Stipendien.Förderende = '0000-00-00 00:00:00')
+GROUP BY Personen.ID
+";
+ $ajax = $nospinner = true;
+ $thdef = ['PersID', 'Nachname', 'Vorname', 'Geschlecht', 'Ort', 'Email', 'Telefon', 'Handy'];
+ $order = '[[2, "asc"], [3, "asc"]]';
+ $checkboxes = true;
+ $idcell = "PersID";
+ include __DIR__ . '/autotable.php';
+
+ $pdf_sql = $email_sql = "SELECT *, ID AS PersID FROM Personen WHERE ID IN ";
+ include __DIR__ . '/autoactions.php';
+ include __DIR__ . '/autoemail.php';
+ include __DIR__ . '/autopdf.php';
+ include __DIR__ . '/autoevent.php';
+})();
+
+include_once __DIR__ . "/footer.php";
diff --git a/template.email.php b/template.email.php
new file mode 100644
index 0000000..9c76425
--- /dev/null
+++ b/template.email.php
@@ -0,0 +1,63 @@
+<?php
+/*
+ * 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.
+ */
+?>
+<?php
+/* vim: set ts=4 sw=4 et : */
+
+require_once __DIR__ . "/check_auth.php";
+require_once __DIR__ . "/../includes/common.php";
+session_write_close();
+require_once __DIR__ . "/lookup.php";
+require_once __DIR__ . "/lookup_more.php";
+
+(function () use ($mysqli) {
+ global $eventID;
+
+ $sql = "
+SELECT
+ email_templates.id AS ID,
+ email_templates.*,
+ email_templates_fav.tid AS etfav
+FROM email_templates
+LEFT JOIN email_templates_fav ON email_templates.id = email_templates_fav.tid AND email_templates_fav.userid = ?
+WHERE email_templates.id=?
+";
+ $stmt = $mysqli->prepare($sql);
+ $stmt->bind_param('ii', $_SESSION['auth_userid'], $_GET['id']);
+ $stmt->execute();
+ $r = $stmt->get_result();
+ $l = $r->fetch_object();
+
+ $_title = 'E-Mail-Template: "<span class="pii">' . $l->name . '</span>" (ID ' . $l->ID . ')';
+ include_once __DIR__ . "/header.php";
+?>
+
+<h1 style="display: inline-block;"><?=$_title?></h1>
+
+<?php
+ $id = "emailtemplate";
+ $def = [
+ [4, '=3name~Name', '_ts'],
+ [6, '+active~Aktiv', '+etfav~pers. Favorit <small>(' . $_SESSION['auth_user'] . ')</small>', '=4tags'],
+ ['from', 'cc', 'bcc'],
+ ['%subject'],
+ ['*text'],
+ ];
+ $entrytable = "email_templates";
+
+ include __DIR__ . '/autoform.php';
+ $stmt->reset();
+})();
+
+include_once __DIR__ . "/footer.php";
+?>
diff --git a/templater_common.php b/templater_common.php
new file mode 100644
index 0000000..7f6a684
--- /dev/null
+++ b/templater_common.php
@@ -0,0 +1,567 @@
+<?php
+/*
+ * 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.
+ */
+?>
+<?
+/* vim: set ts=4 sw=4 et : */
+
+require_once __DIR__ . "/lookup.php";
+require_once __DIR__ . "/lookup_more.php";
+
+(function () use ($mysqli, $sql, $payload_sql, $independent_payload_sql, $payload_sql_order, $independent_payload_sql_order, $isemail, $post_sql) {
+global $abschlusskombis, $studiengaenge, $signaturen;
+
+$stips_sql = "SELECT Person FROM Stipendien WHERE Jahr >= 2001 AND Förderbeginn > '2000-01-01' GROUP BY Person;";
+$stmt = $mysqli->prepare($stips_sql);
+$stmt->bind_result($stips_persid);
+$stmt->execute();
+$stips = [];
+while ($stmt->fetch()) {
+ $stips[$stips_persid] = 1;
+}
+$stmt->reset();
+?>
+<script>
+var Stips = {};
+try {
+ Stips = JSON.parse('<? echo str_replace("'", "\'", str_replace("\\", "\\\\", json_encode($stips))); ?>');
+} catch (e) { ; }
+</script>
+<?
+
+$prefs_sql = "SELECT persid, salutation FROM Personen_Prefs WHERE userid=?;";
+$stmt = $mysqli->prepare($prefs_sql);
+$stmt->bind_param("i", $_SESSION['auth_userid']);
+$stmt->bind_result($prefs_persid, $prefs_salutation);
+$stmt->execute();
+$prefs = [];
+while ($stmt->fetch()) {
+ $prefs[$prefs_persid] = $prefs_salutation;
+}
+$stmt->reset();
+?>
+<script>
+var Prefs = {};
+try {
+ Prefs = JSON.parse('<? echo str_replace("'", "\'", str_replace("\\", "\\\\", json_encode($prefs))); ?>');
+} catch (e) { ; }
+</script>
+<?
+
+$ids = explode(',', $_POST['ids']);
+
+$ids_in = "(";
+$first = true;
+foreach ($ids as $i) {
+ if (!$first) $ids_in .= ', ';
+ $first = false;
+ $ids_in .= "'" . $i . "'";
+}
+$ids_in .= ")";
+
+$sql = $sql . ' ' . $ids_in;
+$sql = $sql . ' ' . $post_sql;
+
+if ($isemail && isset($_SESSION['singleemail'])) {
+?>
+ <div style="border: 1px solid grey; padding: .2em; margin-top: .5em; max-width: 700px;">
+ <b>Einzel-E-Mail-Modul ist aktiv. Alle Adressaten werden zusammengefasst und sind direkter Adressat (To) der E-Mail.</b> Da sich damit alle Adressaten gegenseitig sehen können, sollte diese Funktion nur verwendet werden, wenn dies datenschutzrechtlich unbedenklich ist. Individualisierung durch Templateauszeichnungen ist selbstredent nicht möglich.
+ <br />
+ <button class="small" style="background: lightcoral;" onclick="$.ajaxSetup({async:false}); $.post('/db/main/nosingleemail.php'); location.reload(); return false;">Einzel-E-Mail-Modus beenden</button>
+ </div>
+<?
+ $sql = "SELECT GROUP_CONCAT(q.Email) AS Email FROM (" . $sql . ") q";
+}
+
+$stmt = $mysqli->prepare($sql);
+$stmt->execute();
+$r = $stmt->get_result();
+echo $mysqli->error;
+
+$data = [];
+$i = 0;
+while (($l = $r->fetch_object())) {
+ $e = [];
+ foreach ($l as $k => $v) {
+ $e[$k] = $v;
+ if ($k == 's2' || $k == 's3' || $k == 's4' || $k == 's5' || $k == 's6' || $k == 'data')
+ $e[$k] = json_decode($v);
+ }
+ $e['index'] = $i;
+ $data[] = $e;
+ $i++;
+}
+$stmt->reset();
+
+if ($payload_sql) {
+ $payload_sql = $payload_sql . $ids_in;
+ if ($payload_sql_order)
+ $payload_sql .= " ORDER BY " . $payload_sql_order;
+ $stmt = $mysqli->prepare($payload_sql);
+ $stmt->execute();
+ $r = $stmt->get_result();
+ echo $mysqli->error;
+
+ $payload = [];
+ $i = 0;
+ while (($l = $r->fetch_object())) {
+ $e = [];
+ foreach ($l as $k => $v) {
+ $e[$k] = $v;
+ }
+ $e['index'] = $i;
+ $payload[] = $e;
+ $i++;
+ }
+ $stmt->reset();
+}
+
+if ($independent_payload_sql) {
+ if ($independent_payload_sql_order)
+ $independent_payload_sql .= " ORDER BY " . $independent_payload_sql_order;
+ $stmt = $mysqli->prepare($independent_payload_sql);
+ $stmt->execute();
+ $r = $stmt->get_result();
+ echo $mysqli->error;
+
+ $independent_payload = [];
+ $i = 0;
+ while (($l = $r->fetch_object())) {
+ $e = [];
+ foreach ($l as $k => $v) {
+ $e[$k] = $v;
+ }
+ $e['index'] = $i;
+ $independent_payload[] = $e;
+ $i++;
+ }
+ $stmt->reset();
+}
+?>
+
+<h3 style="margin-bottom: 0;">Template <i style="cursor: pointer;" onclick="$('#template_info').toggle();" class="fas fa-info-circle"></i></h3>
+
+<p id="template_info" style="display: none;" class="explain">
+ Felder mit <code class="explain">{{ d.pfad.feld }}</code> auszeichnen, z.B. <code class="example">{{ d.s2.Nachname }}</code> &ndash; oder unten aktuell verfügbare Felder klicken zum Einf&uuml;gen an Cursorposition
+ <br />
+ Zeilenumbruch statt Absatz mit <code class="explain">Umschalt-Enter</code> oder besser nachvollziehbar mit <code class="example">[br]</code>
+ <br />
+ Seitenumbruch im PDF mit <code class="explain">[formfeed]</code>
+ <br />
+ HTML-Tags mit <code class="explain">[]</code> umschlie&szlig;en, z.B. <code class="example">[hr]</code> (f&uuml;r horizontale Trennlinie) oder <code class="example">[div style="border: 1px solid black;"]Box[/div]</code> (für div-Element mit 1px Rand)
+ <br />
+ Fallunterscheidung: <code class="explain">{{ d.Jahr == '2013' ? 'Ja, 2013.' : 'Nein, nicht das Jahr 2013' }}</code>
+ <br />
+ Signatur: <code class="explain">{{ 'Hagemann' | signatur }}</code>
+ <br />
+ Automatische Anrede: <code class="explain">{{ 'informal' | autoanrede }}</code> oder <code class="explain">{{ 'formal' | autoanrede }}</code>
+ <br />
+ Filter: <code class="explain">{{ d.Feld | filter }}</code>. Verfügbar:
+ <br />
+ - <code class="explain">filter</code> (generischer Filter, filtert Liste auf <span style="color: lightcoral;">Wert</span> in <span style="color: darkgoldenrod;">Attribut</span>. Bsp.: <code class="example">{{ p | filter:<span style="color: darkgoldenrod;">'persid'</span>:<span style="color: lightcoral;">d.ID</span> }}</code>)
+ <br />
+ - <code class="explain">event</code> (Teilnahmelink zum Event, <code class="example">{{ d.token | event }}</code>)
+ <br />
+ - <code class="explain">eventlist</code> (Ausgabe Eventliste samt Anmeldelink f&uuml;r Person aus Menge gew&auml;hlter Events, bei der die jew. Person eingetragen ist; <code class="example">{{ d.ID | eventlist }}</code>, optionale Parameter: minPartID, CatID, Reihenfolge umdrehen (<code class="example">{{ d.ID | eventlist:0:0:1 }}</code>)
+ <br />
+ - <code class="explain">studiengang</code> (Studiengang anhand ID aus Abschlusskombis, z.B. <code class="example">{{ d.s3.studiengang | studiengang }}</code>, für alte Studiengänge: <code class="example">studiengang_alt</code>)
+ <br />
+ - <code class="explain">my</code> (Link zum Pers&ouml;nlichen Bereich DS anhand UID)
+ <br />
+ - <code class="explain">mysocial</code> (Link zum Pers&ouml;nlichen Bereich Sozialstipendium anhand UID)
+ <br />
+ - <code class="explain">mylue</code> (Link zum Pers&ouml;nlichen Bereich Leistungs&uuml;berpr&uuml;er anhand PersID)
+ <br />
+ - <code class="explain">lue24</code> (Teilnahmelink Leistungs&uuml;berpr&uuml;fung 2024 anhand UID)
+ <br />
+ - <code class="explain">dscontinue</code> (Fortf&uuml;hrlink DS anhand UID)
+ <br />
+ - <code class="explain">socialcontinue</code> (Fortf&uuml;hrlink Sozialstipendium anhand UID)
+ <br />
+ - <code class="explain">dsnachreichung</code> (Link Nachreichungsbereich DS anhand UID)
+ <br />
+ - <code class="explain">alumni21</code> (Link Alumni-Umfrage 2021 anhand PersID)
+ <br />
+ - <code class="explain">komm21</code> (Link Kommissionsmitglieder-Umfrage 2021 anhand PersID)
+ <br />
+ - <code class="explain">{{ x | stiplist:y:z:fb }}</code> (Stipendiatenliste. Parameter: x&rarr;FoerdID, y&rarr;Förderart (0=alle,5=Sozial), z&rarr;Art der Ausgabe (0/1=kompakt, 2=mit Förderzeitraum, 3=mit Förderzeitraum und Förderart, 4=mit Förderzeitraum und vereinfachter Förderart (DS/Sozial), 5=mit Teilnahme bei Event (Niklas muss das vorher explizit einstellen!), fb&rarr;erst ab Förderbeginn JJJJ-MM-TT Bsp.: <code class="example">{{ d.FoerdID | stiplist:0:4:'2023-04-01' }}</code>
+ <br />
+ Verfügbare Felder:
+<?
+foreach ($data[0] as $k => $v) {
+?>
+ <a style="text-decoration: underline; color: #0c8ab3 !important; cursor: pointer;" onclick="quill.focus(); quill.insertText(quill.getSelection(true), '{{ d[\'<?=$k?>\'] }}');">d.<?=$k?></a>
+<?
+ if (is_object($data[0][$k])) {
+ foreach ($data[0][$k] as $kk => $v) {
+?>
+ <small>.<a style="text-decoration: underline; cursor: pointer;" onclick="quill.focus(); quill.insertText(quill.getSelection(true), '{{ d[\'<?=$k?>\'][\'<?=$kk?>\'] }}');"><?=$kk?></a></small>
+<?
+ }
+ }
+}
+?>
+</p>
+
+<div class="editorcontainer" style="background: white; color: black;">
+ <div id="editor"><? if ($isemail) {?>{{ '<?=$_SESSION['auth_user']?>' | signatur }}<? } ?></div>
+</div>
+
+<?
+(function () use ($mysqli) {
+ $sql = "
+SELECT id, title
+FROM pages
+WHERE visible = 1 AND oid = 2
+ORDER BY title
+";
+ $stmt = $mysqli->prepare($sql);
+ $stmt->bind_result($pid, $ptitle);
+ $stmt->execute();
+?>
+ <textarea style="display: none;" class="htmlcontainer" style="background: darkbeige !important; color: grey; height: 1em; width: 3em;"></textarea>
+
+<?
+})();
+?>
+
+<script>
+<? minStart(); ?>
+
+var Delta = Quill.import('delta');
+var Break = Quill.import('blots/break');
+var Embed = Quill.import('blots/embed');
+
+function lineBreakMatcher() {
+ var newDelta = new Delta();
+ newDelta.insert({'break': ''});
+ return newDelta;
+}
+
+class SmartBreak extends Break {
+ length () {
+ return 1;
+ }
+ value () {
+ return '\n';
+ }
+ insertInto(parent, ref) {
+ Embed.prototype.insertInto.call(this, parent, ref);
+ }
+}
+SmartBreak.blotName = 'break';
+SmartBreak.tagName = 'BR'
+Quill.register(SmartBreak);
+
+var quill = new Quill('#editor', {
+ theme: 'snow',
+ modules: {
+ toolbar: [
+ [{ header: [1, 2, 3, false] }],
+ [{'size': ['small', false, 'large', 'huge']}],
+ ['bold', 'italic', 'underline', 'strike'],
+ [{ 'color': [] }, { 'background': [] }],
+ ['link', 'blockquote', 'image'],
+ [{'list': 'ordered'}, {'list': 'bullet'}],
+ [{'script': 'sub'}, {'script': 'super'}],
+ [{'indent': '-1'}, {'indent': '+1'}],
+ [{'align': []}],
+ ['clean']
+ ],
+ clipboard: {
+ matchers: [
+ ['BR', lineBreakMatcher]
+ ]
+ },
+ keyboard: {
+ bindings: {
+ linebreak: {
+ key: 13,
+ shiftKey: true,
+ handler: function (range) {
+ var currentLeaf = this.quill.getLeaf(range.index)[0];
+ var nextLeaf = this.quill.getLeaf(range.index + 1)[0];
+
+ this.quill.insertEmbed(range.index, 'break', true, 'user');
+
+ if (nextLeaf === null || (currentLeaf.parent !== nextLeaf.parent)) {
+ this.quill.insertEmbed(range.index, 'break', true, 'user');
+ }
+
+ this.quill.setSelection(range.index + 1, Quill.sources.SILENT);
+ }
+ }
+ }
+ }
+ }
+});
+
+function redoRows(row, d, pum, index, dindex) {
+ if (typeof row === 'undefined')
+ return;
+
+ if (typeof data !== 'object')
+ return;
+
+ if (data[dindex].__done) {
+ row.classList.add('email_success');
+ row.classList.remove('email_failed');
+ }
+ if (data[dindex].__failed) {
+ row.classList.add('email_failed');
+ row.classList.remove('email_success');
+ }
+ if (data[dindex].__skip) {
+ row.classList.add('validated');
+ row.classList.remove('email_failed');
+ row.classList.remove('email_success');
+ }
+}
+
+<? minEnd(); ?>
+</script>
+
+<h3 style="margin-bottom: 0;">Vorschau</h3>
+
+<style>
+ #preview h1, #preview h2, #preview h3, #preview h4, #preview p, #preview * { font-family: "Arial Narrow", "Arial", sans-serif; }
+ #preview ul, #preview ol, #preview li, #preview strong, #preview em, #preview i, #preview b, #preview u, #preview s, #preview span, #preview sub, #preview sup { font-family: "Arial Narrow", "Arial", sans-serif; }
+ #preview blockquote { font-family: "Arial Narrow", "Arial", sans-serif; }
+
+ #preview .ql-size-small { font-size: 70%; }
+ #preview .ql-size-normal { font-size: 100%; }
+ #preview .ql-size-large { font-size: 130%; }
+ #preview .ql-size-huge { font-size: 180%; }
+
+ #preview a { color: blue; text-color: blue; }
+ .ql-editor { padding: 0; }
+</style>
+
+<div id="previewcontainer" style="background: white; color: black; border: 1px solid lightgrey; padding: .5em;">
+ <div id="preview" ng-controller="previewCtrl"></div>
+</div>
+
+<h3 style="margin-bottom: 0;">Adressaten <i onclick="$('#recipient_info').toggle();" class="fas fa-info-circle"></i></h3>
+
+<div id="recipient_info" style="display: none;">
+<p>Mit Klick auf blaue RUID/UID/ID die Vorschau zum entsprechenden Adressaten wechseln.</p>
+<p>Bei Mehrfachverknüpfungen (bspw. Person bei Orga) RUID/UID verwenden, ansonsten wird die Vorschau nur für das angeklickte Merkmal angepasst.</p>
+</div>
+
+<?
+(function () use ($mysqli, $sql) {
+ $stmt = $mysqli->prepare($sql);
+ $stmt->execute();
+ $r = $stmt->get_result();
+ $id = "email";
+ $order = '[]';
+ $noautolinks = true;
+ $nodefaultlinks = true;
+ $links = [
+ '#' => '#',
+ 'RUID' => '#',
+ 'ruid' => '#',
+ 'UID' => '#',
+ 'uid' => '#',
+ 'ID' => '#',
+ 'id' => '#',
+ ];
+ $rowcallback = "redoRows";
+ $norefresh = true;
+ include __DIR__ . '/autotable.php';
+ $stmt->reset();
+})();
+?>
+
+<script>
+
+window.data = JSON.parse('<?php echo str_replace("'", "\'", str_replace("\\", "\\\\", json_encode($data))); ?>');
+window.payload = JSON.parse('<?php echo str_replace("'", "\'", str_replace("\\", "\\\\", json_encode($payload))); ?>');
+window.independent_payload = JSON.parse('<?php echo str_replace("'", "\'", str_replace("\\", "\\\\", json_encode($independent_payload))); ?>');
+window.pivot = data[0];
+window.count_s = 0;
+window.count_f = 0;
+
+function bP() {
+ $('.consignee .changeable').parent().parent().css('background', 'unset');
+
+ var dd = $('#editor .ql-editor').html();
+ dd = dd.replace(/┌/g, '<').replace(/┐/g, '>');
+ $('#previewcontainer').html('<div id="preview">' + dd + '</div>');
+ angular.bootstrap($('#preview'), ['previewApp']);
+ angular.element('#preview').scope().d = pivot;
+ angular.element('#preview').scope().p = payload;
+ angular.element('#preview').scope().ip = independent_payload;
+ angular.element('#preview').scope().$apply();
+
+ var a = $('#preview').html();
+ a = a.replace(/\[/g, '<').replace(/\]/g, '>');
+ $('#preview').html(a);
+
+ try { $('input[name=organisation]').val(pivot.organisation || pivot.Organisation); } catch (e) { }
+ try { $('input[name=anrede]').val(pivot.anrede || pivot.Anrede); } catch(e) { }
+ try { $('input[name=anrede_briefkopf]').val(pivot.anrede_briefkopf); } catch (e) { }
+ try { $('input[name=titel]').val(pivot.titel || pivot.Titel); } catch (e) { }
+ try { $('input[name=vorname]').val(pivot.vorname || pivot.Vorname); } catch (e) { }
+ try { $('input[name=nachname]').val(pivot.nachname || pivot.Nachname); } catch (e) { }
+ try { $('input[name=strasse]').val(pivot.strasse || pivot.Strasse || pivot.Straße); } catch (e) { }
+ try { $('input[name=adresszusatz]').val(pivot.adresszusatz || pivot.Adresszusatz); } catch (e) { }
+ try { $('input[name=plz]').val(pivot.plz || pivot.PLZ); } catch (e) { }
+ try { $('input[name=template]').val(pivot.hochschule || pivot.Hochschule); } catch (e) { }
+ try { $('input[name=ort]').val(pivot.ort || pivot.Ort); } catch (e) { }
+ try { $('input[name=uid]').val(pivot.genuid || pivot.RUID || pivot.ruid || pivot.ID || pivot.id || pivot.UID || pivot.uid); } catch (e) { }
+ try { $('input[name=email_to]').val(pivot.email || pivot.Email); } catch (e) { }
+ try { $('input[name=email_id]').val(pivot.uid || pivot.ID || pivot.id); } catch (e) { }
+ try { $('input[name=data]').val(pivot.data || JSON.stringify(pivot)); } catch (e) { }
+}
+
+$(document).ready(function () {
+ window.preview = angular.module('previewApp', []);
+ window.previewCtrl = preview.controller('previewCtrl', ['$scope', function ($scope) {
+ $scope.d = data[0];
+ }]);
+
+ $('textarea.htmlcontainer').on('input change', function () {
+ if ($('textarea.htmlcontainer').val().length > 10) {
+ $('.editorcontainer').hide();
+ $('#previewcontainer').hide();
+ return;
+ }
+ $('.editorcontainer').show();
+ $('#previewcontainer').show();
+ });
+
+ $('.consignee .changeable').on('input change', function () {
+ $(this).parent().parent().css('background', 'yellow');
+ });
+
+ preview.filter('autoanrede', function () {
+ return function (x) {
+ var persid = "";
+
+ if (typeof pivot.PersID !== 'undefined') persid = pivot.PersID;
+ else if (typeof pivot.persid !== 'undefined') persid = pivot.persid;
+ else if (typeof pivot.ID !== 'undefined') persid = pivot.ID;
+ else if (typeof pivot.id !== 'undefined') persid = pivot.id;
+
+ var titel = pivot.Titel;
+ if (titel == null) titel = "";
+
+ var anrede = pivot.Anrede;
+ if (anrede == null) anrede = "";
+
+ var r = "";
+
+ try {
+
+ if ((x === 'informal' && Prefs[persid] !== 1) || Prefs[persid] === 2 || Prefs[persid] === 3 || (Prefs[persid] !== 1 && Stips[persid] === 1)) {
+ if (pivot.Geschlecht.charAt(0) === 'm') {
+ r += "Lieber ";
+ } else if (pivot.Geschlecht.charAt(0) === 'w') {
+ r += "Liebe ";
+ } else {
+ r += "Hallo ";
+ }
+ if (Prefs[persid] !== 3 && Stips[persid] !== 1) {
+ if (pivot.Geschlecht.charAt(0) === 'm') {
+ r += "Herr ";
+ } else if (pivot.Geschlecht.charAt(0) === 'w') {
+ r += "Frau ";
+ }
+ }
+ if (Prefs[persid] === 3 || Stips[persid] === 1) {
+ r += pivot.Vorname;
+ } else {
+ if (titel.indexOf('Prof') !== -1) {
+ if (pivot.Geschlecht.charAt(0) == 'w') {
+ r += "Professorin ";
+ } else {
+ r += "Professor ";
+ }
+ } else if (titel.indexOf('Dr.') !== -1) {
+ r += "Dr. ";
+ } else if (titel != ""){
+ r += titel + " ";
+ }
+ r += pivot.Nachname;
+ }
+ r += ',';
+
+ if (anrede.indexOf("Eheleute") !== -1 || anrede.indexOf("Ehepaar") !== -1) {
+ r = pivot['informale Ansprache'] + " " + pivot.Nachname + ",";
+ }
+ } else if (x === 'formal' || Prefs[persid] == 1) {
+ if (pivot.Geschlecht.charAt(0) === 'm') {
+ r += "Sehr geehrter Herr ";
+ } else if (pivot.Geschlecht.charAt(0) === 'w') {
+ r += "Sehr geehrte Frau ";
+ }
+
+ if (titel.indexOf('Prof') !== -1) {
+ r += "Professor ";
+ } else if (titel.indexOf('Dr.') !== -1) {
+ r += "Dr. ";
+ } else if (titel != ""){
+ r += titel + " ";
+ }
+ r += pivot.Nachname;
+ r += ',';
+
+ if (anrede.indexOf("Eheleute") !== -1 || anrede.indexOf("Ehepaar") !== -1) {
+ r = pivot.Ansprache + " " + pivot.Nachname + ",";
+ }
+ }
+
+ } catch (e) {
+ r = "Hallo " + pivot.Vorname + " " + pivot.Nachname + ",";
+ }
+
+ return r;
+ }
+ });
+
+ preview.filter('filter', function () {
+ return function (x, y, z) {
+ var d = $.grep(x, function (v, i) {
+ return v[y] == z;
+ });
+ return d;
+ }
+ });
+
+ quill.on('text-change', bP);
+ bP();
+});
+
+$(window).on('hashchange', function () {
+ var r = $.grep(data, function (e) {
+ return (e.ruid == location.hash.substr(1) || e.RUID == location.hash.substr(1) || e.uid == location.hash.substr(1) || e.UID == location.hash.substr(1) || e.ID == location.hash.substr(1) || e.id == location.hash.substr(1));
+ });
+ pivot = r[0];
+ bP();
+});
+
+</script>
+
+<style>
+::placeholder {
+ color: lightgrey;
+}
+.mand:placeholder-shown {
+ border: 1px solid red;
+ padding: 1px;
+}
+</style>
+
+<?
+})();
+?>
diff --git a/templates.email.php b/templates.email.php
new file mode 100644
index 0000000..6ab8e31
--- /dev/null
+++ b/templates.email.php
@@ -0,0 +1,72 @@
+<?php
+/*
+ * 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.
+ */
+?>
+<?
+/* vim: set ts=4 sw=4 et : */
+
+$_title = 'E-Mail-Templates';
+
+require_once __DIR__ . "/check_auth.php";
+require_once __DIR__ . "/header.php";
+require_once __DIR__ . "/../includes/common.php";
+
+doTitle();
+
+(function () {
+ $id = "emailtemplates";
+ $sql = "
+SELECT id AS ETID, name, ts, active, tags, `from`, subject, text
+FROM email_templates
+";
+ $getthdef = true;
+ $idcell = "ETID";
+ $idcellreal = "id";
+ $entrytable = 'email_templates';
+ $types = [
+ 'active' => 'checkbox'
+ ];
+ $editable = [
+ 'active',
+ 'name',
+ 'from',
+ 'tags',
+ ];
+ $checkboxes = true;
+
+ $bottom = <<<EOD
+<div class="admin_actionsbelow">
+ Ausgewählte
+ <button class="medium" style="background: darkorange;" onclick="return delET();"><i class="fas fa-trash-alt"></i> entfernen</button>
+</div>
+EOD;
+
+ include __DIR__ . '/autotable.php';
+?>
+<script>
+function delET() {
+ $.ajaxSetup({async:false});
+ $.ajax({
+ type: 'POST',
+ url: '/db/main/delet.php',
+ data: {
+ 'ids': getIDs_<?=$id?>()
+ }
+ });
+ window.location.reload();
+ return false;
+}
+</script>
+<?
+})();
+
+include_once __DIR__ . "/footer.php";
diff --git a/unwrap-php.sh b/unwrap-php.sh
new file mode 100755
index 0000000..f587828
--- /dev/null
+++ b/unwrap-php.sh
@@ -0,0 +1,5 @@
+#!/bin/sh
+
+sed=`which gsed 2>/dev/null || which sed`
+
+$sed -i -e 's/&lt;?/<?/g' -e 's/?&gt;/?>/g' "$1"
diff --git a/update.php b/update.php
new file mode 100644
index 0000000..e0e738a
--- /dev/null
+++ b/update.php
@@ -0,0 +1,95 @@
+<?php
+/*
+ * 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.
+ */
+?>
+<?php
+/* vim: set ts=4 sw=4 et : */
+
+require_once __DIR__ . "/check_auth.php";
+require_once __DIR__ . "/../includes/common.php";
+
+if (!isset($_POST['table']) || !isset($_POST['n']) || !isset($_POST['v']) || !isset($_POST['id'])) {
+ echo "false";
+ $mysqli->close();
+ exit(0);
+}
+
+$t = filter_input(INPUT_POST, 'table', FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_BACKTICK);
+$n = filter_input(INPUT_POST, 'n', FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_BACKTICK);
+
+$bps = "si";
+if (isset($_POST['num']) && $_POST['num'] == 'true')
+ $bps = "ii";
+
+if ($n === 'etfav' && $t === 'email_templates') {
+ if ($_POST['v'] < 1) {
+ $sql = "DELETE FROM email_templates_fav WHERE tid=? AND userid=?";
+ $stmt = $mysqli->prepare($sql);
+ $stmt->bind_param("ii", $_POST['id'], $_SESSION['auth_userid']);
+ $stmt->execute();
+ $stmt->reset();
+ } else {
+ $sql = "INSERT INTO email_templates_fav (tid, userid) VALUES (?, ?)";
+ $stmt = $mysqli->prepare($sql);
+ $stmt->bind_param("ii", $_POST['id'], $_SESSION['auth_userid']);
+ $stmt->execute();
+ $stmt->reset();
+ }
+
+ echo "true";
+
+ $mysqli->close();
+ exit(0);
+}
+
+if ($n === 'salutation' && $t === 'Personen') {
+ $sql = "INSERT INTO Personen_Prefs (persid, userid, salutation) VALUES (?, ?, ?)";
+ $stmt = $mysqli->prepare($sql);
+ $stmt->bind_param("iii", $_POST['id'], $_SESSION['auth_userid'], $_POST['v']);
+ $stmt->execute();
+ $stmt->reset();
+
+ $sql = "UPDATE Personen_Prefs SET salutation=? WHERE persid=? AND userid=?";
+ $stmt = $mysqli->prepare($sql);
+ $stmt->bind_param("iii", $_POST['v'], $_POST['id'], $_SESSION['auth_userid']);
+ $stmt->execute();
+ $stmt->reset();
+
+ echo "true";
+
+ $mysqli->close();
+ exit(0);
+}
+
+$sql = "UPDATE `" . $t . "` SET `" . $n . "`=? WHERE ID=? LIMIT 1;";
+if (isset($_POST['idcell']) && $_POST['idcell'] != 'false') {
+ $idcell = filter_input(INPUT_POST, 'idcell', FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_BACKTICK);
+ $sql = "UPDATE `" . $t . "` SET `" . $n . "`=? WHERE `" . $idcell . "`=? LIMIT 1;";
+ $bps = 'ss';
+ if (isset($_POST['num']) && $_POST['num'] == 'true')
+ $bps = 'is';
+}
+$stmt = $mysqli->prepare($sql);
+#echo $sql . "|" . $bps . "|" . $_POST['v'] . "|" . $_POST['id'] . "@";
+
+$stmt->bind_param($bps, $_POST['v'], $_POST['id']);
+$stmt->execute();
+
+preg_match_all('!\d+!', $mysqli->info, $m);
+if ($m[0][0] == 1 || $m[0][1] == 1 || $stmt->affected_rows == 1)
+ echo "true";
+else
+ echo "false";
+
+$stmt->reset();
+$mysqli->close();
+exit(0);