1
0
mirror of https://github.com/wbbaddons/Tims-Chat.git synced 2024-10-31 14:10:08 +00:00

Compare commits

...

No commits in common. "master" and "v3.0.5" have entirely different histories.

389 changed files with 9499 additions and 23770 deletions

View File

@ -1,16 +0,0 @@
{ "presets": [ [ "@babel/env"
, { "targets": { "browsers": [ "last 2 chrome versions"
, "last 2 chromeandroid versions"
, "firefox esr"
, "last 2 firefox versions"
, "last 2 edge versions"
, "safari >= 15"
, "ios >= 15"
]
}
, "debug": true
, "include": [ ]
}
]
]
}

View File

@ -1 +0,0 @@
3a88e73ee106ef675affb76a6399625bccf446e1

3
.gitattributes vendored
View File

@ -1,3 +0,0 @@
*.php diff=php
*.css diff=css
*.scss diff=css

70
.gitignore vendored
View File

@ -1,13 +1,63 @@
node_modules # Linux
# backup files
*~
be.bastelstu.chat.tar.gz # Windows
be.bastelstu.chat.tar # thumbnails
files.tar Thumbs.db
files_wcf.tar
acptemplates.tar
templates.tar
Bastelstu.be.Chat.js # Mac OS X
Bastelstu.be.Chat.babel.js # metadata
.DS_Store
# thumbnails
._*
files_wcf/js/Bastelstu.be.Chat.min.js # Visual Studio PHP
*.sln
*.phpproj
*.puo
*.suo
*.cache
# Netbeans
nbproject/
catalog.xml
nbactions.xml
# Eclipse
.settings/
.buildpath
.classpath
.project
# SVN
# svn folders
.svn/
# PHPStorm
.idea/
.nameencodings
.xmlmisc
.xmlmodules
.xmlprojectCodeStyle
.xmlvcs.xml
*.imlworkspace
.xml
# Sublime Text 2
*.sublime-*
# Textmate
*.tmproj
# Community Framework
# Ignore packages build directly in the workspace. They can however be added manually via git add, if wanted.
*.tar
*.tar.gz
# Javascript - CoffeeScript is used instead ;)
*.js
.sass-cache
*.css
*.bak

View File

@ -1 +0,0 @@
require.build.js

View File

@ -1,3 +0,0 @@
semi: false
singleQuote: true
useTabs: true

16
.travis.yml Normal file
View File

@ -0,0 +1,16 @@
language: php
php:
- 5.6
- 5.3
before_install:
- pyrus install pear/PHP_CodeSniffer
- phpenv rehash
before_script:
- git clone --branch=master --depth=1 --quiet git://github.com/WoltLab/WCF.git WCF
- rm WCF/CodeSniff/WCF/Sniffs/Namespaces/ClassMustBeImportedSniff.php
- rm WCF/CodeSniff/WCF/Sniffs/Namespaces/UseDeclarationSniff.php
- sed -i 's/<rule ref="WCF.Namespaces.ClassMustBeImported" \/>//' WCF/CodeSniff/WCF/ruleset.xml
- sed -i 's/<rule ref="WCF.Namespaces.UseDeclaration" \/>//' WCF/CodeSniff/WCF/ruleset.xml
script:
- find file -type f -name '*.php' |xargs -I file php -l file
- phpcs -p --extensions=php --standard="`pwd`/WCF/CodeSniff/WCF" file

View File

@ -1,8 +0,0 @@
{
"editor.rulers": [
{
"column": 120,
"color": "#ff00ff40"
}
]
}

433
LICENSE
View File

@ -1,104 +1,383 @@
License text copyright (c) 2017 MariaDB Corporation Ab, All Rights Reserved. The following license applies to Tims Chat in whole:
“Business Source License” is a trademark of MariaDB Corporation Ab.
Parameters The name "Tims Chat" may not be used in any derivated work, except for the
purpose of giving proper attribution to the original authors (e.g. “Based on
Tims Chat, developed in Tims Bastelstube”). Tims Chat in a whole is licensed
under the terms of
Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported
with exceptions made for some files, as specified below.
Licensor: Tim Düsterhus -------------------------------------------------------------------------------
Licensed Work: Tims Chat 4.3
The Licensed Work is (c) 2010-2024 Tim Düsterhus
Additional Use Grant: You may use the Licensed Work when your application
uses the Licensed Work for a purpose that does neither
directly or indirectly generate revenue.
Change Date: 2028-06-15 The following license applies to all artwork, logos, images and any other
digital asset, unless specified otherwise in the file itself:
Change License: Version 2 or later of the GNU General Public License as The files may ONLY be used in the context of and distributed together with an
published by the Free Software Foundation. UNMODIFIED copy of Tims Chat. Any further RESTRICTION that applies to Tims Chat
in a whole (i.e. attribution, prohibition of commercial usage, etc.; see below)
applies to the artwork, logos, images and any other digital asset as well, any
further PERMISSION that applies to Tims Chat in a whole DOES NOT apply to the
artwork, logos, images and any other digital asset. Any use not explicitly
allowed by this license is PROHIBITED.
For information about alternative licensing arrangements for the Software, If any provision of this License is invalid or unenforceable under
please email: tim <at the domain> bastelstu.be applicable law, it shall not affect the validity or enforceability of
the remainder of the terms of this License, and without further action
by the parties to this agreement, such provision shall be reformed to
the minimum extent necessary to make such provision valid and
enforceable.
Notice -------------------------------------------------------------------------------
The Business Source License (this document, or the “License”) is not an Open Creative Commons Legal Code
Source license. However, the Licensed Work will eventually be made available
under an Open Source License, as stated in this License.
For more information on the use of the Business Source License for MariaDB Attribution-NonCommercial-ShareAlike 3.0 Unported
products, please visit the MariaDB Business Source License FAQ at
https://mariadb.com/bsl-faq-mariadb.
For more information on the use of the Business Source License generally, THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE
please visit the Adopting and Developing Business Source License FAQ at COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY
https://mariadb.com/bsl-faq-adopting. COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS
AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED.
----------------------------------------------------------------------------- BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE
TO BE BOUND BY THE TERMS OF THIS LICENSE. TO THE EXTENT THIS LICENSE MAY
BE CONSIDERED TO BE A CONTRACT, THE LICENSOR GRANTS YOU THE RIGHTS
CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND
CONDITIONS.
Business Source License 1.1 1. Definitions
Terms a. "Adaptation" means a work based upon the Work, or upon the Work and
other pre-existing works, such as a translation, adaptation,
derivative work, arrangement of music or other alterations of a
literary or artistic work, or phonogram or performance and includes
cinematographic adaptations or any other form in which the Work may be
recast, transformed, or adapted including in any form recognizably
derived from the original, except that a work that constitutes a
Collection will not be considered an Adaptation for the purpose of
this License. For the avoidance of doubt, where the Work is a musical
work, performance or phonogram, the synchronization of the Work in
timed-relation with a moving image ("synching") will be considered an
Adaptation for the purpose of this License.
b. "Collection" means a collection of literary or artistic works, such as
encyclopedias and anthologies, or performances, phonograms or
broadcasts, or other works or subject matter other than works listed
in Section 1(g) below, which, by reason of the selection and
arrangement of their contents, constitute intellectual creations, in
which the Work is included in its entirety in unmodified form along
with one or more other contributions, each constituting separate and
independent works in themselves, which together are assembled into a
collective whole. A work that constitutes a Collection will not be
considered an Adaptation (as defined above) for the purposes of this
License.
c. "Distribute" means to make available to the public the original and
copies of the Work or Adaptation, as appropriate, through sale or
other transfer of ownership.
d. "License Elements" means the following high-level license attributes
as selected by Licensor and indicated in the title of this License:
Attribution, Noncommercial, ShareAlike.
e. "Licensor" means the individual, individuals, entity or entities that
offer(s) the Work under the terms of this License.
f. "Original Author" means, in the case of a literary or artistic work,
the individual, individuals, entity or entities who created the Work
or if no individual or entity can be identified, the publisher; and in
addition (i) in the case of a performance the actors, singers,
musicians, dancers, and other persons who act, sing, deliver, declaim,
play in, interpret or otherwise perform literary or artistic works or
expressions of folklore; (ii) in the case of a phonogram the producer
being the person or legal entity who first fixes the sounds of a
performance or other sounds; and, (iii) in the case of broadcasts, the
organization that transmits the broadcast.
g. "Work" means the literary and/or artistic work offered under the terms
of this License including without limitation any production in the
literary, scientific and artistic domain, whatever may be the mode or
form of its expression including digital form, such as a book,
pamphlet and other writing; a lecture, address, sermon or other work
of the same nature; a dramatic or dramatico-musical work; a
choreographic work or entertainment in dumb show; a musical
composition with or without words; a cinematographic work to which are
assimilated works expressed by a process analogous to cinematography;
a work of drawing, painting, architecture, sculpture, engraving or
lithography; a photographic work to which are assimilated works
expressed by a process analogous to photography; a work of applied
art; an illustration, map, plan, sketch or three-dimensional work
relative to geography, topography, architecture or science; a
performance; a broadcast; a phonogram; a compilation of data to the
extent it is protected as a copyrightable work; or a work performed by
a variety or circus performer to the extent it is not otherwise
considered a literary or artistic work.
h. "You" means an individual or entity exercising rights under this
License who has not previously violated the terms of this License with
respect to the Work, or who has received express permission from the
Licensor to exercise rights under this License despite a previous
violation.
i. "Publicly Perform" means to perform public recitations of the Work and
to communicate to the public those public recitations, by any means or
process, including by wire or wireless means or public digital
performances; to make available to the public Works in such a way that
members of the public may access these Works from a place and at a
place individually chosen by them; to perform the Work to the public
by any means or process and the communication to the public of the
performances of the Work, including by public digital performance; to
broadcast and rebroadcast the Work by any means including signs,
sounds or images.
j. "Reproduce" means to make copies of the Work by any means including
without limitation by sound or visual recordings and the right of
fixation and reproducing fixations of the Work, including storage of a
protected performance or phonogram in digital form or other electronic
medium.
The Licensor hereby grants you the right to copy, modify, create derivative 2. Fair Dealing Rights. Nothing in this License is intended to reduce,
works, redistribute, and make non-production use of the Licensed Work. The limit, or restrict any uses free from copyright or rights arising from
Licensor may make an Additional Use Grant, above, permitting limited limitations or exceptions that are provided for in connection with the
production use. copyright protection under copyright law or other applicable laws.
Effective on the Change Date, or the fourth anniversary of the first publicly 3. License Grant. Subject to the terms and conditions of this License,
available distribution of a specific version of the Licensed Work under this Licensor hereby grants You a worldwide, royalty-free, non-exclusive,
License, whichever comes first, the Licensor hereby grants you rights under perpetual (for the duration of the applicable copyright) license to
the terms of the Change License, and the rights granted in the paragraph exercise the rights in the Work as stated below:
above terminate.
If your use of the Licensed Work does not comply with the requirements a. to Reproduce the Work, to incorporate the Work into one or more
currently in effect as described in this License, you must purchase a Collections, and to Reproduce the Work as incorporated in the
commercial license from the Licensor, its affiliated entities, or authorized Collections;
resellers, or you must refrain from using the Licensed Work. b. to create and Reproduce Adaptations provided that any such Adaptation,
including any translation in any medium, takes reasonable steps to
clearly label, demarcate or otherwise identify that changes were made
to the original Work. For example, a translation could be marked "The
original work was translated from English to Spanish," or a
modification could indicate "The original work has been modified.";
c. to Distribute and Publicly Perform the Work including as incorporated
in Collections; and,
d. to Distribute and Publicly Perform Adaptations.
All copies of the original and modified Licensed Work, and derivative works The above rights may be exercised in all media and formats whether now
of the Licensed Work, are subject to this License. This License applies known or hereafter devised. The above rights include the right to make
separately for each version of the Licensed Work and the Change Date may vary such modifications as are technically necessary to exercise the rights in
for each version of the Licensed Work released by Licensor. other media and formats. Subject to Section 8(f), all rights not expressly
granted by Licensor are hereby reserved, including but not limited to the
rights described in Section 4(e).
You must conspicuously display this License on each original or modified copy 4. Restrictions. The license granted in Section 3 above is expressly made
of the Licensed Work. If you receive the Licensed Work in original or subject to and limited by the following restrictions:
modified form from a third party, the terms and conditions set forth in this
License apply to your use of that work.
Any use of the Licensed Work in violation of this License will automatically a. You may Distribute or Publicly Perform the Work only under the terms
terminate your rights under this License for the current and all other of this License. You must include a copy of, or the Uniform Resource
versions of the Licensed Work. Identifier (URI) for, this License with every copy of the Work You
Distribute or Publicly Perform. You may not offer or impose any terms
on the Work that restrict the terms of this License or the ability of
the recipient of the Work to exercise the rights granted to that
recipient under the terms of the License. You may not sublicense the
Work. You must keep intact all notices that refer to this License and
to the disclaimer of warranties with every copy of the Work You
Distribute or Publicly Perform. When You Distribute or Publicly
Perform the Work, You may not impose any effective technological
measures on the Work that restrict the ability of a recipient of the
Work from You to exercise the rights granted to that recipient under
the terms of the License. This Section 4(a) applies to the Work as
incorporated in a Collection, but this does not require the Collection
apart from the Work itself to be made subject to the terms of this
License. If You create a Collection, upon notice from any Licensor You
must, to the extent practicable, remove from the Collection any credit
as required by Section 4(d), as requested. If You create an
Adaptation, upon notice from any Licensor You must, to the extent
practicable, remove from the Adaptation any credit as required by
Section 4(d), as requested.
b. You may Distribute or Publicly Perform an Adaptation only under: (i)
the terms of this License; (ii) a later version of this License with
the same License Elements as this License; (iii) a Creative Commons
jurisdiction license (either this or a later license version) that
contains the same License Elements as this License (e.g.,
Attribution-NonCommercial-ShareAlike 3.0 US) ("Applicable License").
You must include a copy of, or the URI, for Applicable License with
every copy of each Adaptation You Distribute or Publicly Perform. You
may not offer or impose any terms on the Adaptation that restrict the
terms of the Applicable License or the ability of the recipient of the
Adaptation to exercise the rights granted to that recipient under the
terms of the Applicable License. You must keep intact all notices that
refer to the Applicable License and to the disclaimer of warranties
with every copy of the Work as included in the Adaptation You
Distribute or Publicly Perform. When You Distribute or Publicly
Perform the Adaptation, You may not impose any effective technological
measures on the Adaptation that restrict the ability of a recipient of
the Adaptation from You to exercise the rights granted to that
recipient under the terms of the Applicable License. This Section 4(b)
applies to the Adaptation as incorporated in a Collection, but this
does not require the Collection apart from the Adaptation itself to be
made subject to the terms of the Applicable License.
c. You may not exercise any of the rights granted to You in Section 3
above in any manner that is primarily intended for or directed toward
commercial advantage or private monetary compensation. The exchange of
the Work for other copyrighted works by means of digital file-sharing
or otherwise shall not be considered to be intended for or directed
toward commercial advantage or private monetary compensation, provided
there is no payment of any monetary compensation in con-nection with
the exchange of copyrighted works.
d. If You Distribute, or Publicly Perform the Work or any Adaptations or
Collections, You must, unless a request has been made pursuant to
Section 4(a), keep intact all copyright notices for the Work and
provide, reasonable to the medium or means You are utilizing: (i) the
name of the Original Author (or pseudonym, if applicable) if supplied,
and/or if the Original Author and/or Licensor designate another party
or parties (e.g., a sponsor institute, publishing entity, journal) for
attribution ("Attribution Parties") in Licensor's copyright notice,
terms of service or by other reasonable means, the name of such party
or parties; (ii) the title of the Work if supplied; (iii) to the
extent reasonably practicable, the URI, if any, that Licensor
specifies to be associated with the Work, unless such URI does not
refer to the copyright notice or licensing information for the Work;
and, (iv) consistent with Section 3(b), in the case of an Adaptation,
a credit identifying the use of the Work in the Adaptation (e.g.,
"French translation of the Work by Original Author," or "Screenplay
based on original Work by Original Author"). The credit required by
this Section 4(d) may be implemented in any reasonable manner;
provided, however, that in the case of a Adaptation or Collection, at
a minimum such credit will appear, if a credit for all contributing
authors of the Adaptation or Collection appears, then as part of these
credits and in a manner at least as prominent as the credits for the
other contributing authors. For the avoidance of doubt, You may only
use the credit required by this Section for the purpose of attribution
in the manner set out above and, by exercising Your rights under this
License, You may not implicitly or explicitly assert or imply any
connection with, sponsorship or endorsement by the Original Author,
Licensor and/or Attribution Parties, as appropriate, of You or Your
use of the Work, without the separate, express prior written
permission of the Original Author, Licensor and/or Attribution
Parties.
e. For the avoidance of doubt:
This License does not grant you any right in any trademark or logo of i. Non-waivable Compulsory License Schemes. In those jurisdictions in
Licensor or its affiliates (provided that you may use a trademark or logo of which the right to collect royalties through any statutory or
Licensor as expressly required by this License). compulsory licensing scheme cannot be waived, the Licensor
reserves the exclusive right to collect such royalties for any
exercise by You of the rights granted under this License;
ii. Waivable Compulsory License Schemes. In those jurisdictions in
which the right to collect royalties through any statutory or
compulsory licensing scheme can be waived, the Licensor reserves
the exclusive right to collect such royalties for any exercise by
You of the rights granted under this License if Your exercise of
such rights is for a purpose or use which is otherwise than
noncommercial as permitted under Section 4(c) and otherwise waives
the right to collect royalties through any statutory or compulsory
licensing scheme; and,
iii. Voluntary License Schemes. The Licensor reserves the right to
collect royalties, whether individually or, in the event that the
Licensor is a member of a collecting society that administers
voluntary licensing schemes, via that society, from any exercise
by You of the rights granted under this License that is for a
purpose or use which is otherwise than noncommercial as permitted
under Section 4(c).
f. Except as otherwise agreed in writing by the Licensor or as may be
otherwise permitted by applicable law, if You Reproduce, Distribute or
Publicly Perform the Work either by itself or as part of any
Adaptations or Collections, You must not distort, mutilate, modify or
take other derogatory action in relation to the Work which would be
prejudicial to the Original Author's honor or reputation. Licensor
agrees that in those jurisdictions (e.g. Japan), in which any exercise
of the right granted in Section 3(b) of this License (the right to
make Adaptations) would be deemed to be a distortion, mutilation,
modification or other derogatory action prejudicial to the Original
Author's honor and reputation, the Licensor will waive or not assert,
as appropriate, this Section, to the fullest extent permitted by the
applicable national law, to enable You to reasonably exercise Your
right under Section 3(b) of this License (right to make Adaptations)
but not otherwise.
TO THE EXTENT PERMITTED BY APPLICABLE LAW, THE LICENSED WORK IS PROVIDED ON 5. Representations, Warranties and Disclaimer
AN “AS IS” BASIS. LICENSOR HEREBY DISCLAIMS ALL WARRANTIES AND CONDITIONS,
EXPRESS OR IMPLIED, INCLUDING (WITHOUT LIMITATION) WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, AND
TITLE.
MariaDB hereby grants you permission to use this Licenses text to license UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING AND TO THE
your works, and to refer to it using the trademark “Business Source License”, FULLEST EXTENT PERMITTED BY APPLICABLE LAW, LICENSOR OFFERS THE WORK AS-IS
as long as you comply with the Covenants of Licensor below. AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND CONCERNING THE
WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, INCLUDING, WITHOUT
LIMITATION, WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR
PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT OR OTHER DEFECTS,
ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, WHETHER OR NOT
DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF IMPLIED
WARRANTIES, SO THIS EXCLUSION MAY NOT APPLY TO YOU.
Covenants of Licensor 6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE
LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR
ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES
ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS
BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
In consideration of the right to use this Licenses text and the “Business 7. Termination
Source License” name and trademark, Licensor covenants to MariaDB, and to all
other recipients of the licensed work to be provided by Licensor:
1. To specify as the Change License the GPL Version 2.0 or any later version, a. This License and the rights granted hereunder will terminate
or a license that is compatible with GPL Version 2.0 or a later version, automatically upon any breach by You of the terms of this License.
where “compatible” means that software provided under the Change License can Individuals or entities who have received Adaptations or Collections
be included in a program with software provided under GPL Version 2.0 or a from You under this License, however, will not have their licenses
later version. Licensor may specify additional Change Licenses without terminated provided such individuals or entities remain in full
limitation. compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will
survive any termination of this License.
b. Subject to the above terms and conditions, the license granted here is
perpetual (for the duration of the applicable copyright in the Work).
Notwithstanding the above, Licensor reserves the right to release the
Work under different license terms or to stop distributing the Work at
any time; provided, however that any such election will not serve to
withdraw this License (or any other license that has been, or is
required to be, granted under the terms of this License), and this
License will continue in full force and effect unless terminated as
stated above.
2. To either: (a) specify an additional grant of rights to use that does not 8. Miscellaneous
impose any additional restriction on the right granted in this License, as
the Additional Use Grant; or (b) insert the text “None”.
3. To specify a Change Date. a. Each time You Distribute or Publicly Perform the Work or a Collection,
the Licensor offers to the recipient a license to the Work on the same
terms and conditions as the license granted to You under this License.
b. Each time You Distribute or Publicly Perform an Adaptation, Licensor
offers to the recipient a license to the original Work on the same
terms and conditions as the license granted to You under this License.
c. If any provision of this License is invalid or unenforceable under
applicable law, it shall not affect the validity or enforceability of
the remainder of the terms of this License, and without further action
by the parties to this agreement, such provision shall be reformed to
the minimum extent necessary to make such provision valid and
enforceable.
d. No term or provision of this License shall be deemed waived and no
breach consented to unless such waiver or consent shall be in writing
and signed by the party to be charged with such waiver or consent.
e. This License constitutes the entire agreement between the parties with
respect to the Work licensed here. There are no understandings,
agreements or representations with respect to the Work not specified
here. Licensor shall not be bound by any additional provisions that
may appear in any communication from You. This License may not be
modified without the mutual written agreement of the Licensor and You.
f. The rights granted under, and the subject matter referenced, in this
License were drafted utilizing the terminology of the Berne Convention
for the Protection of Literary and Artistic Works (as amended on
September 28, 1979), the Rome Convention of 1961, the WIPO Copyright
Treaty of 1996, the WIPO Performances and Phonograms Treaty of 1996
and the Universal Copyright Convention (as revised on July 24, 1971).
These rights and subject matter take effect in the relevant
jurisdiction in which the License terms are sought to be enforced
according to the corresponding provisions of the implementation of
those treaty provisions in the applicable national law. If the
standard suite of rights granted under applicable copyright law
includes additional rights not granted under this License, such
additional rights are deemed to be included in the License; this
License is not intended to restrict the license of any rights under
applicable law.
4. Not to modify this License in any other way.
Creative Commons Notice
Creative Commons is not a party to this License, and makes no warranty
whatsoever in connection with the Work. Creative Commons will not be
liable to You or any party on any legal theory for any damages
whatsoever, including without limitation any general, special,
incidental or consequential damages arising in connection to this
license. Notwithstanding the foregoing two (2) sentences, if Creative
Commons has expressly identified itself as the Licensor hereunder, it
shall have all rights and obligations of Licensor.
Except for the limited purpose of indicating to the public that the
Work is licensed under the CCPL, Creative Commons does not authorize
the use by either party of the trademark "Creative Commons" or any
related trademark or logo of Creative Commons without the prior
written consent of Creative Commons. Any permitted use will be in
compliance with Creative Commons' then-current trademark usage
guidelines, as may be published on its website or otherwise made
available upon request from time to time. For the avoidance of doubt,
this trademark restriction does not form part of this License.
Creative Commons may be contacted at http://creativecommons.org/.

View File

@ -1,46 +0,0 @@
FILES = $(shell find files -type f)
WCF_FILES = $(shell find files_wcf -type f)
JS_MODULE_FILES = $(shell find files_wcf/js/Bastelstu.be -type f)
all: be.bastelstu.chat.tar be.bastelstu.chat.tar.gz
be.bastelstu.chat.tar.gz: be.bastelstu.chat.tar
gzip -9 < $< > $@
be.bastelstu.chat.tar: files.tar files_wcf.tar acptemplates.tar templates.tar *.xml LICENSE sql/*.sql language/*.xml
tar cvf $@ --mtime="@$(shell git log -1 --format=%ct)" --owner=0 --group=0 --numeric-owner --exclude-vcs -- $^
files.tar: $(FILES)
files_wcf.tar: $(WCF_FILES) files_wcf/js/Bastelstu.be.Chat.min.js
acptemplates.tar: acptemplates/*.tpl
templates.tar: templates/*.tpl
%.tar:
tar cvf $@ --mtime="@$(shell git log -1 --format=%ct)" --owner=0 --group=0 --numeric-owner --exclude-vcs -C $* -- $(^:$*/%=%)
files_wcf/js/Bastelstu.be.Chat.min.js: Bastelstu.be.Chat.babel.js
yarn run terser --comments '/Copyright|stackoverflow/' -m -c pure_funcs=[console.debug] --verbose --timings -o $@ $^
Bastelstu.be.Chat.babel.js: Bastelstu.be.Chat.js .babelrc
yarn run babel $< --out-file $@
Bastelstu.be.Chat.js: $(JS_MODULE_FILES)
yarn run r_js -o require.build.js
clean:
-rm -f files.tar
-rm -f files_wcf.tar
-rm -f templates.tar
-rm -f acptemplates.tar
-rm -f Bastelstu.be.Chat.js
-rm -f Bastelstu.be.Chat.babel.js
-rm -f files_wcf/js/Bastelstu.be.Chat.min.js
distclean: clean
-rm -f be.bastelstu.chat.tar
-rm -f be.bastelstu.chat.tar.gz
constants.php: option.xml
(echo "<?php" ; xq -r '.data.import.options.option[] | "define(\"" + (.["@name"] | ascii_upcase) + "\", " + .defaultvalue + ");"' < option.xml) > constants.php
.PHONY: distclean clean

10
README.md Normal file
View File

@ -0,0 +1,10 @@
Tims Chat 3.0
==============
Tims Chat is a chat-plugin for WoltLab Community Framework.
License
-------
For licensing information refer to the LICENSE file in this folder.

View File

@ -9,13 +9,9 @@
<objecttype>be.bastelstu.chat.room</objecttype> <objecttype>be.bastelstu.chat.room</objecttype>
</category> </category>
</categories> </categories>
<options> <options>
<option name="user.canSee"> <option name="user.canEnter">
<objecttype>be.bastelstu.chat.room</objecttype>
<categoryname>user</categoryname>
</option>
<option name="user.canSeeLog">
<objecttype>be.bastelstu.chat.room</objecttype> <objecttype>be.bastelstu.chat.room</objecttype>
<categoryname>user</categoryname> <categoryname>user</categoryname>
</option> </option>
@ -23,7 +19,13 @@
<objecttype>be.bastelstu.chat.room</objecttype> <objecttype>be.bastelstu.chat.room</objecttype>
<categoryname>user</categoryname> <categoryname>user</categoryname>
</option> </option>
<option name="mod.canIgnoreUserLimit">
<option name="mod.canAlwaysEnter">
<objecttype>be.bastelstu.chat.room</objecttype>
<categoryname>mod</categoryname>
</option>
<option name="mod.canAlwaysWrite">
<objecttype>be.bastelstu.chat.room</objecttype> <objecttype>be.bastelstu.chat.room</objecttype>
<categoryname>mod</categoryname> <categoryname>mod</categoryname>
</option> </option>
@ -31,18 +33,10 @@
<objecttype>be.bastelstu.chat.room</objecttype> <objecttype>be.bastelstu.chat.room</objecttype>
<categoryname>mod</categoryname> <categoryname>mod</categoryname>
</option> </option>
<option name="mod.canIgnoreMute">
<objecttype>be.bastelstu.chat.room</objecttype>
<categoryname>mod</categoryname>
</option>
<option name="mod.canBan"> <option name="mod.canBan">
<objecttype>be.bastelstu.chat.room</objecttype> <objecttype>be.bastelstu.chat.room</objecttype>
<categoryname>mod</categoryname> <categoryname>mod</categoryname>
</option> </option>
<option name="mod.canIgnoreBan">
<objecttype>be.bastelstu.chat.room</objecttype>
<categoryname>mod</categoryname>
</option>
</options> </options>
</import> </import>
</data> </data>

View File

@ -2,41 +2,39 @@
<data xmlns="http://www.woltlab.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.woltlab.com http://www.woltlab.com/XSD/maelstrom/acpMenu.xsd"> <data xmlns="http://www.woltlab.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.woltlab.com http://www.woltlab.com/XSD/maelstrom/acpMenu.xsd">
<import> <import>
<acpmenuitem name="chat.acp.menu.link.chat"> <acpmenuitem name="chat.acp.menu.link.chat">
<parent>wcf.acp.menu.link.application</parent> <parent>wcf.acp.menu.link.community</parent>
</acpmenuitem> </acpmenuitem>
<acpmenuitem name="chat.acp.menu.link.room.list"> <acpmenuitem name="chat.acp.menu.link.room.list">
<controller>chat\acp\page\RoomListPage</controller> <controller><![CDATA[chat\acp\page\RoomListPage]]></controller>
<parent>chat.acp.menu.link.chat</parent> <parent>chat.acp.menu.link.chat</parent>
<permissions>admin.chat.canManageRoom</permissions> <permissions>admin.chat.canEditRoom,admin.chat.canDeleteRoom</permissions>
<showorder>1</showorder> <showorder>1</showorder>
</acpmenuitem> </acpmenuitem>
<acpmenuitem name="chat.acp.menu.link.room.add"> <acpmenuitem name="chat.acp.menu.link.room.add">
<controller>chat\acp\form\RoomAddForm</controller> <controller><![CDATA[chat\acp\form\RoomAddForm]]></controller>
<parent>chat.acp.menu.link.room.list</parent>
<permissions>admin.chat.canManageRoom</permissions>
<icon>plus</icon>
</acpmenuitem>
<acpmenuitem name="chat.acp.menu.link.command.trigger.list">
<controller>chat\acp\page\CommandTriggerListPage</controller>
<parent>chat.acp.menu.link.chat</parent> <parent>chat.acp.menu.link.chat</parent>
<permissions>admin.chat.canManageTriggers</permissions> <permissions>admin.chat.canAddRoom</permissions>
<showorder>2</showorder>
</acpmenuitem> </acpmenuitem>
<acpmenuitem name="chat.acp.menu.link.command.trigger.add">
<controller>chat\acp\form\CommandTriggerAddForm</controller>
<parent>chat.acp.menu.link.command.trigger.list</parent>
<permissions>admin.chat.canManageTriggers</permissions>
<icon>plus</icon>
</acpmenuitem>
<acpmenuitem name="chat.acp.menu.link.suspension.list"> <acpmenuitem name="chat.acp.menu.link.suspension.list">
<controller>chat\acp\page\SuspensionListPage</controller> <controller><![CDATA[chat\acp\page\ChatSuspensionListPage]]></controller>
<parent>chat.acp.menu.link.chat</parent> <parent>chat.acp.menu.link.chat</parent>
<permissions>admin.chat.canManageSuspensions</permissions> <permissions>admin.chat.canManageSuspensions</permissions>
<showorder>3</showorder>
</acpmenuitem>
<acpmenuitem name="chat.acp.menu.link.log">
<controller><![CDATA[chat\acp\page\MessageLogPage]]></controller>
<parent>chat.acp.menu.link.chat</parent>
<permissions>admin.chat.canReadLog</permissions>
<showorder>4</showorder>
</acpmenuitem> </acpmenuitem>
</import> </import>
<delete>
<acpmenuitem name="chat.acp.menu.link" />
</delete>
</data> </data>

View File

@ -0,0 +1 @@
{if $__chat->isActiveApplication()}<img src="{$__wcf->getPath('chat')}images/chatLogo.png" style="width: 246px; height: 90px;" alt="" />{/if}

View File

@ -0,0 +1,26 @@
{if $messages|count > 0}
<table class="table">
<thead>
<tr>
<th>{lang}wcf.global.objectID{/lang}</th>
<th>{lang}chat.global.time{/lang}</th>
<th colspan="2">{lang}wcf.user.username{/lang}</th>
<th>{lang}chat.acp.log.message{/lang}</th>
</tr>
</thead>
<tbody>
{foreach from=$messages item="message"}
<tr>
<td class="columnID">{$message->messageID}</td>
<td style="width: 1px !important;">{$message->time|date:"H:i:s"}</td>
<td class="columnIcon"><p class="framed">{@$message->getUserProfile()->getAvatar()->getImageTag(24)}</p></td>
<td class="columnTitle columnUsername right" style="width: 1px !important;">{$message->username}</td>
<td>{@$message->getFormattedMessage("text/simplified-html")}</td>
</tr>
{/foreach}
</tbody>
</table>
{else}
<p class="info">{lang}wcf.global.noItems{/lang}</p>
{/if}

View File

@ -0,0 +1,4 @@
<dl>
<dt>{lang}wcf.acp.index.system.software.chatVersion{/lang}</dt>
<dd>{$__chat->getPackage()->packageVersion}</dd>
</dl>

View File

@ -0,0 +1,166 @@
{include file='header' pageTitle='chat.acp.suspension.list'}
<script data-relocate="true">
//<![CDATA[
$(function() {
new WCF.Search.User('#username', null, false, [ ], false);
new WCF.Search.User('#issuerUsername', null, false, [ ], false);
var proxy = new WCF.Action.Proxy({
success: function (data, textStatus, jqXHR) {
$('.jsSuspensionRow').each(function(index, row) {
var row = $(row);
if (WCF.inArray(row.data('objectID'), data.objectIDs)) {
row.find('.jsRevokeButton').addClass('disabled').removeClass('pointer').off('click');
(new WCF.System.Notification('{"chat.acp.suspension.revoke.success"|language|encodeJS}')).show();
}
});
}
});
$('.jsRevokeButton:not(.disabled)').click(function () {
var objectID = $(this).parents('.jsSuspensionRow').data('objectID');
WCF.System.Confirmation.show($(this).data('confirmMessage'), $.proxy(function (action) {
if (action === 'confirm') {
proxy.setOption('data', {
actionName: 'revoke',
className: '\\chat\\data\\suspension\\SuspensionAction',
objectIDs: [ objectID ]
});
proxy.sendRequest();
}
}, this));
});
});
//]]>
</script>
<header class="boxHeadline">
<h1>{lang}chat.acp.suspension.list{/lang}</h1>
</header>
<form method="post" action="{link controller='ChatSuspensionList' application='chat'}{/link}">
<div class="container containerPadding marginTop">
<fieldset>
<legend>{lang}wcf.global.filter{/lang}</legend>
<dl>
<dd>
<label><input type="checkbox" id="displayRevoked" name="displayRevoked" value="1"{if $displayRevoked} checked="checked"{/if} /> {lang}chat.acp.suspension.displayRevoked{/lang}</label>
</dd>
</dl>
<dl>
<dt><label for="username">{lang}wcf.user.username{/lang}</label></dt>
<dd>
<input type="text" id="username" name="username" class="medium" value="{$username}" />
</dd>
</dl>
<dl>
<dt><label for="issuerUsername">{lang}chat.acp.suspension.issuer{/lang}</label></dt>
<dd>
<input type="text" id="issuerUsername" name="issuerUsername" class="medium" value="{$issuerUsername}" />
</dd>
</dl>
<dl>
<dt><label for="roomID">{lang}chat.global.room{/lang}</label></dt>
<dd>
<select id="roomID" name="roomID">
<option value="-1"{if $roomID == -1} selected="selected"{/if}></option>
<option value="0"{if $roomID == 0} selected="selected"{/if}>{lang}chat.room.global{/lang}</option>
<option value="" disabled="disabled">-------------</option>
{foreach from=$availableRooms key=id item=room}
<option value="{$id}" {if $roomID == $id}selected="selected"{/if}>{$room}</option>
{/foreach}
</select>
</dd>
</dl>
<dl>
<dt><label for="suspensionType">{lang}chat.acp.suspension.type{/lang}</label></dt>
<dd>
<select id="suspensionType" name="suspensionType">
<option value=""{if $suspensionType === null} selected="selected"{/if}></option>
<option value="{'\chat\data\suspension\Suspension::TYPE_MUTE'|constant}"{if $suspensionType == '\chat\data\suspension\Suspension::TYPE_MUTE'|constant} selected="selected"{/if}>{lang}chat.suspension.{'\chat\data\suspension\Suspension::TYPE_MUTE'|constant}{/lang}</option>
<option value="{'\chat\data\suspension\Suspension::TYPE_BAN'|constant}"{if $suspensionType == '\chat\data\suspension\Suspension::TYPE_BAN'|constant} selected="selected"{/if}>{lang}chat.suspension.{'\chat\data\suspension\Suspension::TYPE_BAN'|constant}{/lang}</option>
</select>
</dd>
</dl>
</fieldset>
</div>
<div class="formSubmit">
<input type="submit" value="{lang}wcf.global.button.submit{/lang}" accesskey="s" />
</div>
</form>
{if $objects|count}
{capture assign=additionalParameters}{*
*}{if $userID}&userID={$userID}{/if}{*
*}{if $issuerUserID}&issuerUserID={$issuerUserID}{/if}{*
*}{if $roomID}&roomID={$roomID}{/if}{*
*}{if $suspensionType}&suspensionType={$suspensionType}{/if}{*
*}{if $displayRevoked}&displayRevoked={$displayRevoked}{/if}{*
*}{/capture}
<div class="contentNavigation">
{pages print=true assign=pagesLinks application="chat" controller="ChatSuspensionList" link="pageNo=%d$additionalParameters"}
</div>
<div class="tabularBox tabularBoxTitle marginTop">
<header>
<h2>{lang}chat.acp.suspension.list{/lang} <span class="badge badgeInverse">{#$items}</span></h2>
</header>
<table class="table">
<thead>
<tr>
<th class="columnID{if $sortField == 'suspensionID'} active {@$sortOrder}{/if}" colspan="2"><a href="{link application='chat' controller='ChatSuspensionList'}pageNo={@$pageNo}&sortField=suspensionID&sortOrder={if $sortField == 'suspensionID' && $sortOrder == 'ASC'}DESC{else}ASC{/if}{@$additionalParameters}{/link}">{lang}wcf.global.objectID{/lang}</a></th>
<th class="columnUsername{if $sortField == 'username'} active {@$sortOrder}{/if}"><a href="{link application='chat' controller='ChatSuspensionList'}pageNo={@$pageNo}&sortField=username&sortOrder={if $sortField == 'username' && $sortOrder == 'ASC'}DESC{else}ASC{/if}{@$additionalParameters}{/link}">{lang}wcf.user.username{/lang}</a></th>
<th class="columnRoomID{if $sortField == 'roomID'} active {@$sortOrder}{/if}"><a href="{link application='chat' controller='ChatSuspensionList'}pageNo={@$pageNo}&sortField=roomID&sortOrder={if $sortField == 'roomID' && $sortOrder == 'ASC'}DESC{else}ASC{/if}{@$additionalParameters}{/link}">{lang}chat.global.room{/lang}</a></th>
<th class="columnSuspensionType{if $sortField == 'type'} active {@$sortOrder}{/if}"><a href="{link application='chat' controller='ChatSuspensionList'}pageNo={@$pageNo}&sortField=type&sortOrder={if $sortField == 'type' && $sortOrder == 'ASC'}DESC{else}ASC{/if}{@$additionalParameters}{/link}">{lang}chat.acp.suspension.type{/lang}</a></th>
<th class="columnTime{if $sortField == 'time'} active {@$sortOrder}{/if}"><a href="{link application='chat' controller='ChatSuspensionList'}pageNo={@$pageNo}&sortField=time&sortOrder={if $sortField == 'time' && $sortOrder == 'ASC'}DESC{else}ASC{/if}{@$additionalParameters}{/link}">{lang}chat.global.time{/lang}</a></th>
<th class="columnExpires{if $sortField == 'expires'} active {@$sortOrder}{/if}"><a href="{link application='chat' controller='ChatSuspensionList'}pageNo={@$pageNo}&sortField=expires&sortOrder={if $sortField == 'expires' && $sortOrder == 'ASC'}DESC{else}ASC{/if}{@$additionalParameters}{/link}">{lang}chat.global.expires{/lang}</a></th>
<th class="columnIssuer{if $sortField == 'issuer'} active {@$sortOrder}{/if}"><a href="{link application='chat' controller='ChatSuspensionList'}pageNo={@$pageNo}&sortField=issuer&sortOrder={if $sortField == 'issuer' && $sortOrder == 'ASC'}DESC{else}ASC{/if}{@$additionalParameters}{/link}">{lang}chat.acp.suspension.issuer{/lang}</a></th>
<th class="columnMessage{if $sortField == 'reason'} active {@$sortOrder}{/if}"><a href="{link application='chat' controller='ChatSuspensionList'}pageNo={@$pageNo}&sortField=reason&sortOrder={if $sortField == 'reason' && $sortOrder == 'ASC'}DESC{else}ASC{/if}{@$additionalParameters}{/link}">{lang}chat.acp.suspension.reason{/lang}</a></th>
{event name='columnHeads'}
</tr>
</thead>
<tbody>
{foreach from=$objects item=$suspension}
<tr class="jsSuspensionRow" data-object-id="{$suspension->suspensionID}">
<td class="columnIcon">
<span class="icon icon16 icon-undo{if $suspension->expires <= TIME_NOW} disabled{else} pointer{/if} jsRevokeButton" title="{lang}chat.acp.suspension.revoke{/lang}" data-confirm-message="{lang}chat.acp.suspension.revoke.sure{/lang}"></span>
{event name='rowButtons'}
</td>
<td id="columnID">{#$suspension->suspensionID}</td>
<td id="columnUsername"><a href="{link application='chat' controller='ChatSuspensionList'}userID={$suspension->userID}{/link}">{$suspension->username}</a></td>
<td id="columnRoomID"><a href="{link application='chat' controller='ChatSuspensionList'}roomID={if $suspension->roomID}{$suspension->roomID}{else}0{/if}{/link}">{if $suspension->roomID}{$suspension->roomTitle|language}{else}{lang}chat.room.global{/lang}{/if}</a></td>
<td id="columnSuspensionType"><a href="{link application='chat' controller='ChatSuspensionList'}suspensionType={$suspension->type}{/link}">{lang}chat.suspension.{@$suspension->type}{/lang}</a></td>
<td id="columnTime">{$suspension->time|plainTime}</td>
<td id="columnExpires">
<p>{$suspension->expires|plainTime}{if $suspension->expires > TIME_NOW} ({$suspension->expires|dateDiff}){/if}</p>
{if $suspension->revoker && $suspension->expires <= TIME_NOW}<p><small>{lang}chat.acp.suspension.revokedBy{/lang}</small></p>{/if}
</td>
<td id="columnIssuer"><a href="{link application='chat' controller='ChatSuspensionList'}issuerUserID={$suspension->issuer}{/link}">{$suspension->issuerUsername}</a></td>
<td id="columnMessage"{if $suspension->reason != $suspension->reason|truncate:30} class="jsTooltip" title="{$suspension->reason}"{/if}>{$suspension->reason|truncate:30}</a></td>
{event name='columns'}
</tr>
{/foreach}
</tbody>
</table>
</div>
<div class="contentNavigation">
{@$pagesLinks}
</div>
{else}
<p class="info">{lang}wcf.global.noItems{/lang}</p>
{/if}
{include file='footer'}

109
acptemplate/messageLog.tpl Normal file
View File

@ -0,0 +1,109 @@
{include file='header' pageTitle='chat.acp.log.title'}
{if CHAT_LOG_ARCHIVETIME !== 0}
<script data-relocate="true" src="{$__wcf->getPath('chat')}acp/js/be.bastelstu.Chat.ACP.Log.js?version={PACKAGE_VERSION|rawurlencode}"></script>
{if $errorField === ''}
<script data-relocate="true">
//<![CDATA[
$(function() {
be.bastelstu.Chat.ACP.Log.TabMenu.init();
WCF.TabMenu.init();
});
//]]>
</script>
{/if}
{/if}
<header class="boxHeadline">
<h1>{lang}{@$pageTitle}{/lang}</h1>
</header>
{if CHAT_LOG_ARCHIVETIME !== 0}
<form method="post" action="{link controller='MessageLog' application='chat'}{/link}">
<div class="container containerPadding marginTop">
<fieldset>
<legend>{lang}wcf.global.filter{/lang}</legend>
<dl>
<dt><label for="id">{lang}chat.global.room{/lang}</label></dt>
<dd>
<select id="id" name="id">
{foreach from=$rooms item='roomBit'}
<option value="{$roomBit->roomID}"{if $roomBit->roomID == $room->roomID} selected="selected"{/if}>{$roomBit}</option>
{/foreach}
</select>
</dd>
</dl>
<dl{if $errorField == 'date'} class="formError"{/if}>
<dt><label for="date">{lang}chat.global.time{/lang}</label></dt>
<dd>
<input id="date" type="date" name="date"{if CHAT_LOG_ARCHIVETIME != -1} min="{TIME_NOW-CHAT_LOG_ARCHIVETIME*60|date:'Y-m-d'}"{/if} max="{TIME_NOW|date:'Y-m-d'}" value="{$date|date:'Y-m-d'}" />
{if $errorField == 'date'}
<small class="innerError">
{lang}chat.acp.log.date.error.{$errorType}{/lang}
</small>
{/if}
</dd>
</dl>
</fieldset>
</div>
<div class="formSubmit">
<input type="submit" value="{lang}wcf.global.button.submit{/lang}" accesskey="s" />
</div>
</form>
<div class="contentNavigation">
<nav>
<ul>
<li>
<a class="button" href="{link controller='MessageLogDownload' application='chat' object=$room date=$date|date:'Y-m-d'}{/link}">
<span class="icon icon16 icon-download-alt"></span>
<span>{lang}chat.acp.log.download{/lang}</span>
</a>
</li>
</ul>
</nav>
</div>
{if $errorField === ''}
<div id="messageLogContent" class="tabMenuContainer marginTop" data-active="timeTab-0" data-store="activeTabMenuItem" data-base-time="{$date}" data-room-id="{$room->roomID}">
<nav class="tabMenu">
<ul>
{section name=tabLoop loop=24 step=3}
<li>
{assign var=anchor value='timeTab-'|concat:$tabLoop}
<a href="{@$__wcf->getAnchor($anchor)}">{if $tabLoop < 10}0{/if}{$tabLoop}:00 - {if $tabLoop + 2 < 10}0{/if}{$tabLoop + 2}:59</a>
</li>
{/section}
</ul>
</nav>
{section name=contentLoop loop=24 step=3}
<div id="timeTab-{$contentLoop}" class="container containerPadding tabMenuContainer tabMenuContent" data-menu-item="timeTab-{$contentLoop}">
<nav class="menu">
<ul>
{section name=subTabLoop loop=6}
{assign var=subAnchor value='timeTab-'|concat:$contentLoop|concat:'-subTab-'|concat:$subTabLoop}
<li data-hour="{$contentLoop + $subTabLoop / 2|floor}" data-minutes="{($subTabLoop % 2) * 30}">
<a href="{@$__wcf->getAnchor($subAnchor)}">{if $contentLoop + $subTabLoop / 2 < 10}0{/if}{$contentLoop + $subTabLoop / 2|floor}:{if $subTabLoop % 2 == 0}0{/if}{($subTabLoop % 2) * 30} - {if $contentLoop + $subTabLoop / 2 < 10}0{/if}{$contentLoop + $subTabLoop / 2|floor}:{($subTabLoop % 2) * 30 + 29}</a>
</li>
{/section}
</ul>
</nav>
{section name=subTabLoop loop=6}
{assign var=subAnchor value='timeTab-'|concat:$contentLoop|concat:'-subTab-'|concat:$subTabLoop}
<div id="{$subAnchor}" class="hidden subTabMenuContent empty"></div>
{/section}
</div>
{/section}
</div>
{/if}
{else}
<p class="error">{lang}chat.acp.log.error.disabled{/lang}</p>
{/if}
{include file='footer'}

View File

@ -0,0 +1,5 @@
{foreach from=$messages item=$rawMessage}{*
*}{assign var=message value=$rawMessage->jsonify(true)}{*
*}({$message['time']|date:'H:i:s'}) {$message[username]|str_pad:15:' ':STR_PAD_LEFT}{$message[separator]} {$message[message]}
{/foreach}

87
acptemplate/roomAdd.tpl Normal file
View File

@ -0,0 +1,87 @@
{include file='header' pageTitle='chat.acp.room.'|concat:$action}
{include file='aclPermissions'}
{if !$roomID|isset}
{include file='aclPermissionJavaScript' containerID='groupPermissions'}
{else}
{include file='aclPermissionJavaScript' containerID='groupPermissions' objectID=$roomID}
{/if}
<header class="boxHeadline">
<h1>{lang}chat.acp.room.{$action}{/lang}</h1>
</header>
{include file='formError'}
{if $success|isset}
<p class="success">{lang}wcf.global.success.{$action}{/lang}</p>
{/if}
<div class="contentNavigation">
{hascontent}
<nav>
<ul>
{content}
<li><a href="{link application='chat' controller='RoomList'}{/link}" class="button"><span class="icon icon16 icon-list"></span> <span>{lang}chat.acp.menu.link.room.list{/lang}</span></a></li>
{event name='contentNavigationButtonsTop'}
{/content}
</ul>
</nav>
{/hascontent}
</div>
<form method="post" action="{if $action == 'add'}{link application='chat' controller='RoomAdd'}{/link}{else}{link application='chat' controller='RoomEdit' id=$roomID}{/link}{/if}">
<div class="container containerPadding marginTop">
<fieldset>
<legend>{lang}wcf.global.form.data{/lang}</legend>
<dl{if $errorField == 'title'} class="formError"{/if}>
<dt><label for="title">{lang}chat.acp.room.title{/lang}</label></dt>
<dd>
<input type="text" id="title" name="title" value="{$title}" autofocus="autofocus" class="long" />
{if $errorField == 'title'}
<small class="innerError">
{if $errorType == 'empty'}
{lang}wcf.global.form.error.empty{/lang}
{else}
{lang}chat.acp.room.title.error.{@$errorType}{/lang}
{/if}
</small>
{/if}
</dd>
</dl>
{include file='multipleLanguageInputJavascript' elementIdentifier='title' forceSelection=false}
<dl{if $errorField == 'topic'} class="formError"{/if}>
<dt><label for="topic">{lang}chat.acp.room.topic{/lang}</label></dt>
<dd>
<input type="text" id="topic" name="topic" value="{$topic}" class="long" />
{if $errorField == 'topic'}
<small class="innerError">
{if $errorType == 'empty'}
{lang}wcf.global.form.error.empty{/lang}
{else}
{lang}chat.acp.room.topic.error.{@$errorType}{/lang}
{/if}
</small>
{/if}
</dd>
</dl>
{include file='multipleLanguageInputJavascript' elementIdentifier='topic' forceSelection=false}
<dl id="groupPermissions">
<dt>{lang}wcf.acl.permissions{/lang}</dt>
<dd></dd>
</dl>
</fieldset>
</div>
<div class="formSubmit">
<input type="submit" value="{lang}wcf.global.button.submit{/lang}" accesskey="s" />
{@SECURITY_TOKEN_INPUT_TAG}
</div>
</form>
{include file='footer'}

66
acptemplate/roomList.tpl Normal file
View File

@ -0,0 +1,66 @@
{include file='header' pageTitle='chat.acp.room.list'}
<script data-relocate="true">
//<![CDATA[
$(function() {
new WCF.Action.Delete('\\chat\\data\\room\\RoomAction', $('.chatRoomRow'));
new WCF.Sortable.List('roomList', '\\chat\\data\\room\\RoomAction');
});
//]]>
</script>
<header class="boxHeadline">
<h1>{lang}chat.acp.room.list{/lang}</h1>
</header>
<div class="contentNavigation">
{hascontent}
<nav>
<ul>
{content}
{if $__wcf->session->getPermission('admin.chat.canAddRoom')}
<li><a href="{link application='chat' controller='RoomAdd'}{/link}" class="button"><span class="icon icon16 icon-plus"></span> <span>{lang}chat.acp.room.add{/lang}</span></a></li>
{/if}
{event name='contentNavigationButtonsTop'}
{/content}
</ul>
</nav>
{/hascontent}
</div>
{if $objects|count}
<section id="roomList" class="container containerPadding sortableListContainer marginTop">
<ol class="sortableList" data-object-id="0">
{foreach from=$objects item=chatRoom}
<li class="sortableNode sortableNoNesting chatRoomRow" data-object-id="{@$chatRoom->roomID}">
<span class="sortableNodeLabel">
{if $__wcf->session->getPermission('admin.chat.canEditRoom')}
<a href="{link application='chat' controller='RoomEdit' id=$chatRoom->roomID}{/link}">{$chatRoom->title|language}</a>
{else}
{$chatRoom->title|language}
{/if}
<span class="statusDisplay sortableButtonContainer">
{if $__wcf->session->getPermission('admin.chat.canEditRoom')}
<a href="{link application='chat' controller='RoomEdit' id=$chatRoom->roomID}{/link}"><span title="{lang}wcf.global.button.edit{/lang}" class="jsTooltip icon icon16 icon-pencil" /></a>
{/if}
{if $__wcf->session->getPermission('admin.chat.canDeleteRoom')}
<span title="{lang}wcf.global.button.delete{/lang}" class="jsDeleteButton jsTooltip icon icon16 icon-remove" data-object-id="{@$chatRoom->roomID}" data-confirm-message="{lang}chat.acp.room.delete.sure{/lang}" />
{/if}
{event name='itemButtons'}
</span>
</span>
<ol class="sortableList" data-object-id="{@$chatRoom->roomID}"></ol></li>
</li>
{/foreach}
</ol>
<div class="formSubmit">
<button class="button buttonPrimary" data-type="submit">{lang}wcf.global.button.saveSorting{/lang}</button>
</div>
</section>
{else}
<p class="warning">{lang}wcf.global.noItems{/lang}</p>
{/if}
{include file='footer'}

View File

@ -1,68 +0,0 @@
{include file='header' pageTitle='chat.acp.command.trigger.'|concat:$action}
<header class="contentHeader">
<div class="contentHeaderTitle">
<h1 class="contentTitle">{lang}chat.acp.command.trigger.{$action}{/lang}</h1>
</div>
<nav class="contentHeaderNavigation">
<ul>
<li><a href="{link application='chat' controller='CommandTriggerList'}{/link}" class="button">{icon name='list'} <span>{lang}chat.acp.command.trigger.list{/lang}</span></a></li>
{event name='contentHeaderNavigation'}
</ul>
</nav>
</header>
{include file='formNotice'}
<form method="post" action="{if $action == 'add'}{link application='chat' controller='CommandTriggerAdd'}{/link}{else}{link application='chat' controller='CommandTriggerEdit' id=$triggerID}{/link}{/if}">
<div class="section">
<div class="section">
<dl{if $errorField == 'commandTrigger'} class="formError"{/if}>
<dt><label for="commandTrigger">{lang}chat.acp.command.trigger{/lang}</label></dt>
<dd>
<input type="text" id="commandTrigger" name="commandTrigger" value="{$commandTrigger}" autofocus class="medium">
{if $errorField == 'commandTrigger'}
<small class="innerError">
{if $errorType == 'empty'}
{lang}wcf.global.form.error.empty{/lang}
{else}
{lang}chat.acp.command.trigger.commandTrigger.error.{@$errorType}{/lang}
{/if}
</small>
{/if}
</dd>
</dl>
<dl{if $errorField == 'className'} class="formError"{/if}>
<dt><label for="className">{lang}chat.acp.command.className{/lang}</label></dt>
<dd>
<select id="className" name="className">
{foreach from=$availableCommands item=$command}
<option value="{$command->className}"{if $command->className === $className} selected{/if}>{$command->className}</option>
{/foreach}
</select>
{if $errorField == 'className'}
<small class="innerError">
{if $errorType == 'empty'}
{lang}wcf.global.form.error.empty{/lang}
{else}
{lang}chat.acp.command.trigger.className.error.{@$errorType}{/lang}
{/if}
</small>
{/if}
</dd>
</dl>
</div>
</div>
<div class="formSubmit">
<input type="submit" value="{lang}wcf.global.button.submit{/lang}" accesskey="s">
{csrfToken}
</div>
</form>
{include file='footer'}

View File

@ -1,86 +0,0 @@
{include file='header' pageTitle='chat.acp.command.trigger.list'}
<script data-relocate="true">
$(function() {
new WCF.Action.Delete('chat\\data\\command\\CommandTriggerAction', '.jsTriggerRow');
});
</script>
<header class="contentHeader">
<div class="contentHeaderTitle">
<h1 class="contentTitle">{lang}chat.acp.command.trigger.list{/lang}</h1>
</div>
<nav class="contentHeaderNavigation">
<ul>
<li><a href="{link controller='CommandTriggerAdd' application='chat'}{/link}" class="button">{icon name='plus'} <span>{lang}chat.acp.command.trigger.add{/lang}</span></a></li>
{event name='contentHeaderNavigation'}
</ul>
</nav>
</header>
{hascontent}
<div class="paginationTop">
{content}{pages print=true assign=pagesLinks controller="CommandTriggerList" application="chat" link="pageNo=%d&sortField=$sortField&sortOrder=$sortOrder"}{/content}
</div>
{/hascontent}
{hascontent}
<div class="section tabularBox">
<table class="table">
<thead>
<tr>
<th class="columnID columnTriggerID{if $sortField == 'triggerID'} active {@$sortOrder}{/if}" colspan="2"><a href="{link controller='CommandTriggerList' application='chat'}pageNo={@$pageNo}&sortField=triggerID&sortOrder={if $sortField == 'triggerID' && $sortOrder == 'ASC'}DESC{else}ASC{/if}{/link}">{lang}wcf.global.objectID{/lang}</a></th>
<th class="columnTitle columnTrigger{if $sortField == 'commandTrigger'} active {@$sortOrder}{/if}"><a href="{link controller='CommandTriggerList' application='chat'}pageNo={@$pageNo}&sortField=commandTrigger&sortOrder={if $sortField == 'commandTrigger' && $sortOrder == 'ASC'}DESC{else}ASC{/if}{/link}">{lang}chat.acp.command.trigger{/lang}</a></th>
<th class="columnText columnClassName{if $sortField == 'className'} active {@$sortOrder}{/if}"><a href="{link controller='CommandTriggerList' application='chat'}pageNo={@$pageNo}&sortField=className&sortOrder={if $sortField == 'className' && $sortOrder == 'ASC'}DESC{else}ASC{/if}{/link}">{lang}chat.acp.command.className{/lang}</a></th>
{event name='columnHeads'}
</tr>
</thead>
<tbody>
{content}
{foreach from=$objects item=trigger}
<tr class="jsTriggerRow">
<td class="columnIcon">
<a href="{link controller='CommandTriggerEdit' object=$trigger application='chat'}{/link}" title="{lang}wcf.global.button.edit{/lang}" class="jsTooltip">{icon name='pencil'}</a>
<span class="jsDeleteButton jsTooltip pointer" title="{lang}wcf.global.button.delete{/lang}" data-object-id="{$trigger->triggerID}" data-confirm-message-html="{lang __encode=true}chat.acp.command.trigger.delete.sure{/lang}">{icon name='xmark'}</span>
{event name='rowButtons'}
</td>
<td class="columnID">{@$trigger->triggerID}</td>
<td class="columnTitle columnTrigger"><a href="{link controller='CommandTriggerEdit' object=$trigger application='chat'}{/link}">/{$trigger->commandTrigger}</a></td>
<td class="columnText columnClassName">{$trigger->className}</td>
{event name='columns'}
</tr>
{/foreach}
{/content}
</tbody>
</table>
</div>
{hascontentelse}
<p class="info">{lang}wcf.global.noItems{/lang}</p>
{/hascontent}
<footer class="contentFooter">
{hascontent}
<div class="paginationBottom">
{content}{@$pagesLinks}{/content}
</div>
{/hascontent}
<nav class="contentFooterNavigation">
<ul>
<li><a href="{link controller='CommandTriggerAdd' application='chat'}{/link}" class="button">{icon name='plus'} <span>{lang}chat.acp.command.trigger.add{/lang}</span></a></li>
{event name='contentFooterNavigation'}
</ul>
</nav>
</footer>
{include file='footer'}

View File

@ -1,109 +0,0 @@
{include file='header' pageTitle='chat.acp.room.'|concat:$action}
{include file='aclPermissions'}
{include file='multipleLanguageInputJavascript' elementIdentifier='title' forceSelection=false}
{include file='multipleLanguageInputJavascript' elementIdentifier='topic' forceSelection=false}
{if $roomID|isset}
{include file='aclPermissionJavaScript' containerID='aclContainer' objectTypeID=$aclObjectTypeID objectID=$roomID}
{else}
{include file='aclPermissionJavaScript' containerID='aclContainer' objectTypeID=$aclObjectTypeID}
{/if}
<header class="contentHeader">
<div class="contentHeaderTitle">
<h1 class="contentTitle">{lang}chat.acp.room.{$action}{/lang}</h1>
{if $action == 'edit'}<p class="contentHeaderDescription">{$room->getTitle()}</p>{/if}
</div>
<nav class="contentHeaderNavigation">
<ul>
<li><a href="{link application='chat' controller='RoomList'}{/link}" class="button">{icon name='list'} <span>{lang}chat.acp.room.list{/lang}</span></a></li>
{event name='contentHeaderNavigation'}
</ul>
</nav>
</header>
{include file='formNotice'}
<form method="post" action="{if $action == 'add'}{link application='chat' controller='RoomAdd'}{/link}{else}{link application='chat' controller='RoomEdit' id=$roomID}{/link}{/if}">
<div class="section">
<div class="section">
<dl{if $errorField == 'title'} class="formError"{/if}>
<dt><label for="title">{lang}wcf.global.title{/lang}</label></dt>
<dd>
<input type="text" id="title" name="title" value="{$i18nPlainValues['title']}" autofocus class="medium">
{if $errorField == 'title'}
<small class="innerError">
{if $errorType == 'empty'}
{lang}wcf.global.form.error.empty{/lang}
{elseif $errorType == 'multilingual'}
{lang}wcf.global.form.error.multilingual{/lang}
{else}
{lang}chat.acp.room.title.error.{@$errorType}{/lang}
{/if}
</small>
{/if}
</dd>
</dl>
<dl{if $errorField == 'topic'} class="formError"{/if}>
<dt><label for="topic">{lang}chat.acp.room.topic{/lang}</label></dt>
<dd>
<input type="text" id="topic" name="topic" value="{$i18nPlainValues['topic']}" class="long">
{if $errorField == 'topic'}
<small class="innerError">
{if $errorType == 'empty'}
{lang}wcf.global.form.error.empty{/lang}
{else}
{lang}chat.acp.room.topic.error.{@$errorType}{/lang}
{/if}
</small>
{/if}
</dd>
</dl>
<dl{if $errorField == 'topicUseHtml'} class="formError"{/if}>
<dt></dt>
<dd>
<label><input type="checkbox" name="topicUseHtml" value="1"{if $topicUseHtml} checked{/if}> {lang}chat.acp.room.topicUseHtml{/lang}</label>
</dd>
</dl>
<dl{if $errorField == 'userLimit'} class="formError"{/if}>
<dt><label for="userLimit">{lang}chat.acp.room.userLimit{/lang}</label></dt>
<dd>
<input type="number" id="userLimit" name="userLimit" value="{$userLimit}" min="0" class="medium">
{if $errorField == 'userLimit'}
<small class="innerError">
{if $errorType == 'empty'}
{lang}wcf.global.form.error.empty{/lang}
{else}
{lang}chat.acp.room.userLimit.error.{@$errorType}{/lang}
{/if}
</small>
{/if}
</dd>
</dl>
</div>
<div class="section">
<dl id="aclContainer">
<dt>{lang}wcf.acl.permissions{/lang}</dt>
<dd></dd>
</dl>
</div>
</div>
<div class="formSubmit">
<input type="submit" value="{lang}wcf.global.button.submit{/lang}" accesskey="s">
{csrfToken}
</div>
</form>
{include file='footer'}

View File

@ -1,81 +0,0 @@
{include file='header' pageTitle='chat.acp.room.list'}
<script data-relocate="true">
$(function() {
require([ 'WoltLabSuite/Core/Ui/Sortable/List' ], function (UiSortableList) {
new UiSortableList({ containerId: 'roomNodeList'
, className: 'chat\\data\\room\\RoomAction'
})
})
new WCF.Action.Delete('chat\\data\\room\\RoomAction', '#roomNodeList')
})
</script>
<header class="contentHeader">
<div class="contentHeaderTitle">
<h1 class="contentTitle">{lang}chat.acp.room.list{/lang}</h1>
</div>
<nav class="contentHeaderNavigation">
<ul>
<li><a href="{link controller='RoomAdd' application='chat'}{/link}" class="button">{icon name='plus'} <span>{lang}chat.acp.room.add{/lang}</span></a></li>
{event name='contentHeaderNavigation'}
</ul>
</nav>
</header>
{hascontent}
<div class="paginationTop">
{content}{pages print=true assign=pagesLinks controller="RoomList" application="chat" link="pageNo=%d&sortField=$sortField&sortOrder=$sortOrder"}{/content}
</div>
{/hascontent}
{hascontent}
<div id="roomNodeList" class="section sortableListContainer">
<ol id="roomContainer0" class="sortableList" data-object-id="0">
{content}
{foreach from=$objects item=room}
<li class="sortableNode sortableNoNesting" data-object-id="{$room->roomID}">
<span class="sortableNodeLabel">
<a href="{link controller='RoomEdit' application='chat' object=$room}{/link}">{$room}</a>
<span class="statusDisplay sortableButtonContainer">
<span class="sortableNodeHandle">{icon name='up-down-left-right'}</span>
<a href="{link controller='RoomEdit' application='chat' object=$room}{/link}" title="{lang}wcf.global.button.edit{/lang}" class="jsTooltip">{icon name='pencil'}</a>
<span class="jsDeleteButton jsTooltip pointer" title="{lang}wcf.global.button.delete{/lang}" data-object-id="{$room->roomID}" data-confirm-message-html="{lang __encode=true}chat.acp.room.delete.sure{/lang}">{icon name='xmark'}</span>
{event name='itemButtons'}
</span>
</span>
</li>
{/foreach}
{/content}
</ol>
</div>
<div class="formSubmit">
<button class="button buttonPrimary" data-type="submit">{lang}wcf.global.button.saveSorting{/lang}</button>
</div>
{hascontentelse}
<p class="info">{lang}wcf.global.noItems{/lang}</p>
{/hascontent}
<footer class="contentFooter">
{hascontent}
<div class="paginationBottom">
{content}{@$pagesLinks}{/content}
</div>
{/hascontent}
<nav class="contentFooterNavigation">
<ul>
<li><a href="{link controller='RoomAdd' application='chat'}{/link}" class="button">{icon name='plus'} <span>{lang}chat.acp.room.add{/lang}</span></a></li>
{event name='contentFooterNavigation'}
</ul>
</nav>
</footer>
{include file='footer'}

View File

@ -1,192 +0,0 @@
{include file='header' pageTitle='chat.acp.suspension.list'}
<script>
require([ 'Bastelstu.be/PromiseWrap/Ajax', 'Bastelstu.be/PromiseWrap/Ui/Confirmation', 'WoltLabSuite/Core/Dom/Traverse' ], function (Ajax, Confirmation, Traverse) {
elBySelAll('.jsRevokeButton:not(.disabled)', document, function (button) {
const row = Traverse.parentByClass(button, 'jsSuspensionRow')
if (row == null) {
throw new Error('Unreachable')
}
const objectID = row.dataset.objectId
const listener = function (event) {
Confirmation.show({
message: button.dataset.confirmMessageHtml,
messageIsHtml: true
}).then(function () {
const payload = { data: { className: 'chat\\data\\suspension\\SuspensionAction'
, actionName: 'revoke'
, objectIDs: [ objectID ]
}
}
return Ajax.apiOnce(payload)
}).then(function () {
button.classList.remove('pointer')
button.classList.add('disabled')
button.removeEventListener('click', listener)
})
}
button.addEventListener('click', listener)
})
})
</script>
<header class="contentHeader">
<div class="contentHeaderTitle">
<h1 class="contentTitle">{lang}chat.acp.suspension.list{/lang}</h1>
</div>
{hascontent}
<nav class="contentHeaderNavigation">
<ul>
{content}{event name='contentHeaderNavigation'}{/content}
</ul>
</nav>
{/hascontent}
</header>
<form method="post" action="{link controller='SuspensionList' application='chat'}{/link}">
<section class="section">
<h2 class="sectionTitle">{lang}wcf.global.filter{/lang}</h2>
<div class="row rowColGap formGrid">
<dl class="col-xs-12 col-md-4">
<dt></dt>
<dd>
<select name="roomID" id="roomID">
<option value=""{if $roomID === null} selected{/if}>{lang}chat.acp.suspension.room.all{/lang}</option>
<option value="0"{if $roomID === 0} selected{/if}>{lang}chat.acp.suspension.room.global{/lang}</option>
{htmlOptions options=$availableRooms selected=$roomID}
</select>
</dd>
</dl>
<dl class="col-xs-12 col-md-4">
<dt></dt>
<dd>
<select name="objectTypeID" id="objectTypeID">
<option value="">{lang}chat.acp.suspension.objectType.allTypes{/lang}</option>
{foreach from=$availableObjectTypes item=availableObjectType}
<option value="{$availableObjectType->objectTypeID}"{if $availableObjectType->objectTypeID == $objectTypeID} selected{/if}>{lang}chat.acp.suspension.type.{$availableObjectType->objectType}{/lang}</option>
{/foreach}
</select>
</dd>
</dl>
<dl class="col-xs-12 col-md-4">
<dt></dt>
<dd>
<input type="text" id="searchUsername" name="searchUsername" value="{$searchUsername}" placeholder="{lang}chat.acp.suspension.username{/lang}" class="long">
</dd>
</dl>
<dl class="col-xs-12 col-md-4">
<dt></dt>
<dd>
<input type="text" id="searchJudge" name="searchJudge" value="{$searchJudge}" placeholder="{lang}chat.acp.suspension.judge{/lang}" class="long">
</dd>
</dl>
<dl class="col-xs-12 col-md-4">
<dt></dt>
<dd>
<label><input name="showExpired" value="1" type="checkbox"{if $showExpired !== false} checked{/if}>{lang}chat.acp.suspension.showExpired{/lang}</label>
</dd>
</dl>
{event name='filterFields'}
</div>
<div class="formSubmit">
<input type="submit" value="{lang}wcf.global.button.submit{/lang}" accesskey="s">
{csrfToken}
</div>
</section>
</form>
{capture assign=additionalParameters}{*
*}{if $userID !== null}&userID={$userID}{/if}{*
*}{if $judgeID !== null}&judgeID={$judgeID}{/if}{*
*}{if $roomID !== null}&roomID={$roomID}{/if}{*
*}{if $objectTypeID !== null}&objectTypeID={$objectTypeID}{/if}{*
*}{if $showExpired !== null}&showExpired={$showExpired}{/if}{*
*}{/capture}
{hascontent}
<div class="paginationTop">
{content}{pages print=true assign=pagesLinks controller="SuspensionList" application="chat" link="pageNo=%d&sortField=$sortField&sortOrder=$sortOrder$additionalParameters"}{/content}
</div>
{/hascontent}
{hascontent}
<div class="section tabularBox">
<table class="table">
<thead>
<tr>
<th class="columnID columnSuspensionID{if $sortField == 'suspensionID'} active {@$sortOrder}{/if}" colspan="2"><a href="{link controller='SuspensionList' application='chat'}pageNo={@$pageNo}&sortField=suspensionID&sortOrder={if $sortField == 'suspensionID' && $sortOrder == 'ASC'}DESC{else}ASC{/if}{@$additionalParameters}{/link}">{lang}wcf.global.objectID{/lang}</a></th>
<th class="columnTitle columnObjectType">{lang}chat.acp.suspension.type{/lang}</th>
<th class="columnText columnUsername">{lang}chat.acp.suspension.username{/lang}</th>
<th class="columnText columnJudge">{lang}chat.acp.suspension.judge{/lang}</th>
<th class="columnText columnRoom">{lang}chat.acp.suspension.room{/lang}</th>
<th class="columnText columnTime{if $sortField == 'time'} active {@$sortOrder}{/if}"><a href="{link controller='SuspensionList' application='chat'}pageNo={@$pageNo}&sortField=time&sortOrder={if $sortField == 'time' && $sortOrder == 'ASC'}DESC{else}ASC{/if}{@$additionalParameters}{/link}">{lang}chat.acp.suspension.time{/lang}</a></th>
<th class="columnText columnExpires{if $sortField == 'expiresSort'} active {@$sortOrder}{/if}"><a href="{link controller='SuspensionList' application='chat'}pageNo={@$pageNo}&sortField=expiresSort&sortOrder={if $sortField == 'expiresSort' && $sortOrder == 'ASC'}DESC{else}ASC{/if}{@$additionalParameters}{/link}">{lang}chat.acp.suspension.expires{/lang}</a></th>
{event name='columnHeads'}
</tr>
</thead>
<tbody>
{content}
{foreach from=$objects item=suspension}
<tr class="jsSuspensionRow" data-object-id="{$suspension->suspensionID}">
<td class="columnIcon">
<span class="jsRevokeButton{if !$suspension->isActive()} disabled{else} pointer{/if}" title="{lang}chat.acp.suspension.revoke{/lang}" data-confirm-message-html="{lang}chat.acp.suspension.revoke.sure{/lang}">{icon name='arrow-rotate-left'}</span>
{event name='rowButtons'}
</td>
<td class="columnID">{@$suspension->suspensionID}</td>
<td class="columnTitle columnObjectType"><a href="{link controller="SuspensionList" application="chat"}objectTypeID={$suspension->objectTypeID}{/link}">{lang}chat.acp.suspension.type.{$suspension->getSuspensionType()->objectType}{/lang}</a></td>
<td class="columnText columnUsername"><a href="{link controller="SuspensionList" application="chat"}userID={$suspension->userID}{/link}">{$suspension->getUser()->username}</a></td>
<td class="columnText columnJudge"><a href="{link controller="SuspensionList" application="chat"}judgeID={$suspension->judgeID}{/link}">{$suspension->judge}</a></td>
<td class="columnText columnRoom"><a href="{link controller="SuspensionList" application="chat"}roomID={$suspension->roomID}{/link}">{if $suspension->getRoom() !== null}{$suspension->getRoom()}{else}-{/if}</a></td>
<td class="columnText columnTime">{@$suspension->time|time}</td>
<td class="columnText columnExpires">
{assign var='isActive' value=$suspension->isActive()}
{if $isActive}<strong>{/if}
{if $suspension->expires !== null}{@$suspension->expires|time}{else}{lang}chat.acp.suspension.expires.forever{/lang}{/if}
{if $isActive}</strong>{/if}
{if $suspension->revoked !== null}
<br>{lang}chat.acp.suspension.revoked{/lang}
{/if}
</td>
{event name='columns'}
</tr>
{/foreach}
{/content}
</tbody>
</table>
</div>
{hascontentelse}
<p class="info">{lang}wcf.global.noItems{/lang}</p>
{/hascontent}
<footer class="contentFooter">
{hascontent}
<div class="paginationBottom">
{content}{@$pagesLinks}{/content}
</div>
{/hascontent}
{hascontent}
<nav class="contentFooterNavigation">
<ul>
{content}{event name='contentFooterNavigation'}{/content}
</ul>
</nav>
{/hascontent}
</footer>
{include file='footer'}

42
box.xml
View File

@ -1,42 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<data xmlns="http://www.woltlab.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.woltlab.com http://www.woltlab.com/XSD/vortex/box.xsd">
<import>
<box identifier="be.bastelstu.chat.roomListDashboard">
<name language="de">Chaträume (Inhaltsbereich)</name>
<name language="en">Chat Rooms (Content)</name>
<boxType>system</boxType>
<objectType>be.bastelstu.chat.roomList</objectType>
<position>contentTop</position>
<showHeader>1</showHeader>
<visibleEverywhere>0</visibleEverywhere>
<visibilityExceptions>
<page>com.woltlab.wcf.Dashboard</page>
</visibilityExceptions>
<content language="de">
<title>Chaträume</title>
</content>
<content language="en">
<title>Chat Rooms</title>
</content>
</box>
<box identifier="be.bastelstu.chat.roomListSidebar">
<name language="de">Chaträume (Seitenleiste)</name>
<name language="en">Chat Rooms (Sidebar)</name>
<boxType>system</boxType>
<objectType>be.bastelstu.chat.roomList</objectType>
<position>sidebarRight</position>
<showHeader>1</showHeader>
<visibleEverywhere>0</visibleEverywhere>
<visibilityExceptions>
<page>be.bastelstu.chat.Room</page>
</visibilityExceptions>
<content language="de">
<title>Chaträume</title>
</content>
<content language="en">
<title>Chat Rooms</title>
</content>
</box>
</import>
</data>

View File

@ -1,103 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<data xmlns="http://www.woltlab.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<import>
<command name="away">
<classname>chat\system\command\AwayCommand</classname>
<triggers>
<trigger>away</trigger>
</triggers>
</command>
<command name="back">
<classname>chat\system\command\BackCommand</classname>
</command>
<command name="ban">
<classname>chat\system\command\BanCommand</classname>
<triggers>
<trigger>ban</trigger>
</triggers>
</command>
<command name="broadcast">
<classname>chat\system\command\BroadcastCommand</classname>
<triggers>
<trigger>broadcast</trigger>
</triggers>
</command>
<command name="color">
<classname>chat\system\command\ColorCommand</classname>
<triggers>
<trigger>color</trigger>
</triggers>
</command>
<command name="info">
<classname>chat\system\command\InfoCommand</classname>
<triggers>
<trigger>info</trigger>
</triggers>
</command>
<command name="me">
<classname>chat\system\command\MeCommand</classname>
<triggers>
<trigger>me</trigger>
</triggers>
</command>
<command name="mute">
<classname>chat\system\command\MuteCommand</classname>
<triggers>
<trigger>mute</trigger>
</triggers>
</command>
<command name="plain">
<classname>chat\system\command\PlainCommand</classname>
</command>
<command name="team">
<classname>chat\system\command\TeamCommand</classname>
<triggers>
<trigger>team</trigger>
</triggers>
</command>
<command name="temproom">
<classname>chat\system\command\TemproomCommand</classname>
<triggers>
<trigger>temproom</trigger>
</triggers>
</command>
<command name="unban">
<classname>chat\system\command\UnbanCommand</classname>
<triggers>
<trigger>unban</trigger>
</triggers>
</command>
<command name="unmute">
<classname>chat\system\command\UnmuteCommand</classname>
<triggers>
<trigger>unmute</trigger>
</triggers>
</command>
<command name="where">
<classname>chat\system\command\WhereCommand</classname>
<triggers>
<trigger>where</trigger>
</triggers>
</command>
<command name="whisper">
<classname>chat\system\command\WhisperCommand</classname>
<triggers>
<trigger>whisper</trigger>
</triggers>
</command>
</import>
</data>

View File

@ -1,6 +0,0 @@
<?php
define("CHAT_RELOADTIME", 3);
define("CHAT_AUTOAWAYTIME", 0);
define("CHAT_MAX_LENGTH", 500);
define("CHAT_ARCHIVE_AFTER", 90);
define("CHAT_LOG_ARCHIVETIME", 7);

138
contrib/build.php Executable file
View File

@ -0,0 +1,138 @@
#!/usr/bin/env php
<?php
namespace be\bastelstu\chat;
/**
* Builds the Chat
*
* @author Tim Düsterhus
* @copyright 2010-2014 Tim Düsterhus
* @license Creative Commons Attribution-NonCommercial-ShareAlike <http://creativecommons.org/licenses/by-nc-sa/3.0/legalcode>
* @package be.bastelstu.chat
*/
$packageXML = file_get_contents('package.xml');
preg_match('/<version>(.*?)<\/version>/', $packageXML, $matches);
echo "Building Tims Chat $matches[1]\n";
echo str_repeat("=", strlen("Building Tims Chat $matches[1]"))."\n";
echo <<<EOT
Cleaning up
-----------
EOT;
if (file_exists('package.xml.old')) {
file_put_contents('package.xml', file_get_contents('package.xml.old'));
unlink('package.xml.old');
}
if (file_exists('file.tar')) unlink('file.tar');
if (file_exists('template.tar')) unlink('template.tar');
if (file_exists('acptemplate.tar')) unlink('acptemplate.tar');
foreach (glob('file/acp/be.bastelstu.chat.nodePush/lib/*.js') as $nodeFile) unlink($nodeFile);
foreach (glob('file/js/*.js') as $jsFile) unlink($jsFile);
foreach (glob('file/acp/js/*.js') as $jsFile) unlink($jsFile);
if (file_exists('be.bastelstu.chat.tar')) unlink('be.bastelstu.chat.tar');
echo <<<EOT
Building JavaScript
-------------------
EOT;
foreach (glob('file/js/*.{litcoffee,coffee}', GLOB_BRACE) as $coffeeFile) {
echo $coffeeFile."\n";
passthru('coffee -c '.escapeshellarg($coffeeFile), $code);
if ($code != 0) exit($code);
}
echo <<<EOT
Building ACP-JavaScript
-----------------------
EOT;
foreach (glob('file/acp/js/*.{litcoffee,coffee}', GLOB_BRACE) as $coffeeFile) {
echo $coffeeFile."\n";
passthru('coffee -c '.escapeshellarg($coffeeFile), $code);
if ($code != 0) exit($code);
}
echo <<<EOT
Compressing JavaScript
----------------------
EOT;
foreach (glob('file/js/*.js', GLOB_BRACE) as $jsFile) {
echo $jsFile."\n";
passthru('uglifyjs '.escapeshellarg($jsFile).' --screw-ie8 -m -c --verbose --comments -d production=true -o '.escapeshellarg(substr($jsFile, 0, -3).'.min.js'), $code);
if ($code != 0) exit($code);
}
echo <<<EOT
Checking PHP for Syntax Errors
------------------------------
EOT;
chdir('file');
$check = null;
$check = function ($folder) use (&$check) {
if (is_file($folder)) {
if (substr($folder, -4) === '.php') {
passthru('php -l '.escapeshellarg($folder), $code);
if ($code != 0) exit($code);
}
return;
}
$files = glob($folder.'/*');
foreach ($files as $file) {
$check($file);
}
};
$check('.');
echo <<<EOT
Building file.tar
-----------------
EOT;
passthru('tar cvf ../file.tar --exclude=*coffee --exclude-vcs -- *', $code);
if ($code != 0) exit($code);
echo <<<EOT
Building template.tar
---------------------
EOT;
chdir('../template');
passthru('tar cvf ../template.tar *', $code);
if ($code != 0) exit($code);
echo <<<EOT
Building acptemplate.tar
------------------------
EOT;
chdir('../acptemplate');
passthru('tar cvf ../acptemplate.tar *', $code);
if ($code != 0) exit($code);
echo <<<EOT
Building be.bastelstu.chat.tar
------------------------------
EOT;
chdir('..');
file_put_contents('package.xml.old', file_get_contents('package.xml'));
file_put_contents('package.xml', preg_replace('~<date>\d{4}-\d{2}-\d{2}</date>~', '<date>'.date('Y-m-d').'</date>', file_get_contents('package.xml')));
file_put_contents('package.xml', str_replace('</version>', '</version><!-- git id '.trim(shell_exec('git describe --always')).' -->', file_get_contents('package.xml')));
passthru('tar cvf be.bastelstu.chat.tar --exclude=*.old --exclude=file --exclude-vcs --exclude=template --exclude=acptemplate --exclude=contrib -- *', $code);
if (file_exists('package.xml.old')) {
file_put_contents('package.xml', file_get_contents('package.xml.old'));
unlink('package.xml.old');
}
if ($code != 0) exit($code);
if (file_exists('file.tar')) unlink('file.tar');
if (file_exists('template.tar')) unlink('template.tar');
if (file_exists('acptemplate.tar')) unlink('acptemplate.tar');
foreach (glob('file/acp/be.bastelstu.chat.nodePush/lib/*.js') as $nodeFile) unlink($nodeFile);
foreach (glob('file/js/*.js') as $jsFile) unlink($jsFile);
foreach (glob('file/acp/js/*.js') as $jsFile) unlink($jsFile);

59
contrib/icon/chat.svg Normal file
View File

@ -0,0 +1,59 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!--
@author Maximilian Mader
@copyright 2011-2014 Tim Düsterhus
@license Creative Commons Attribution-NonCommercial-ShareAlike <http://creativecommons.org/licenses/by-nc-sa/3.0/legalcode>
@package be.bastelstu.chat
-->
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="16px" height="16px" viewBox="0 0 16 16" xml:space="preserve">
<title>Chat</title>
<desc>Chat Icon (outlined)</desc>
<defs>
<style type="text/css">
<![CDATA[
.Lower .bubble {
fill:none;
stroke:#ffffff;
stroke-opacity:1;
}
.Lower .leaderLine {
fill:#ffffff;
fill-opacity:1;
stroke:none;
}
.Upper .bubble {
fill:none;
stroke:#666666;
stroke-opacity:1;
}
.Upper .leaderLine {
fill:#666666;
fill-opacity:1;
stroke:none;
}
]]>
</style>
</defs>
<g id="IconChat">
<g class="Lower" style="display:inline">
<path class="bubble"
d="M 15,8 A 6,5 0 1 1 3,8 6,5 0 1 1 15,8 z"
transform="matrix(1.0345788,0,0,0.95089627,-1.510083,0.10801843)" />
<path class="leaderLine"
d="m 3.9001344,11.947742 c 0.075049,1.120603 -0.7948094,1.693805 -1.4915533,2.143417 1.0104403,
0.104041 3.4597063,-0.953975 3.9167086,-1.276108 0.00808,-0.0057 -2.4251553,-0.867309 -2.4251553,-0.867309 z" />
</g>
<g class="Upper" style="display:inline">
<path class="bubble"
d="M 15,8 A 6,5 0 1 1 3,8 6,5 0 1 1 15,8 z"
transform="matrix(1.0345788,0,0,0.95089627,-1.5100816,-0.50773368)" />
<path class="leaderLine"
d="m 3.9001358,11.331991 c 0.075049,1.120603 -0.7948094,1.693805 -1.4915533,2.143417 1.0104403,
0.104041 3.4597063,-0.953975 3.9167086,-1.276108 0.00808,-0.0057 -2.4251553,-0.867309 -2.4251553,-0.867309 z" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

BIN
contrib/icon/chat16.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 657 B

BIN
contrib/icon/chat32.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

BIN
contrib/icon/chat48.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

BIN
contrib/icon/chatL.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

BIN
contrib/icon/chatM.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
contrib/icon/chatS.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 795 B

9
dashboardBox.xml Normal file
View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<data xmlns="http://www.woltlab.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.woltlab.com http://www.woltlab.com/XSD/dashboardBox.xsd">
<import>
<dashboardbox name="be.bastelstu.chat.onlineList">
<classname><![CDATA[chat\system\dashboard\box\OnlineListDashboardBox]]></classname>
<boxtype>content</boxtype>
</dashboardbox>
</import>
</data>

View File

@ -1,166 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<data xmlns="http://www.woltlab.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.woltlab.com http://www.woltlab.com/XSD/maelstrom/eventListener.xsd"> <data xmlns="http://www.woltlab.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.woltlab.com http://www.woltlab.com/XSD/maelstrom/eventListener.xsd">
<import> <import>
<!-- Hourly Cleanup --> <eventlistener>
<eventlistener name="hourlyCleanUpUser">
<eventclassname>wcf\system\cronjob\HourlyCleanUpCronjob</eventclassname> <eventclassname>wcf\system\cronjob\HourlyCleanUpCronjob</eventclassname>
<eventname>execute</eventname> <eventname>execute</eventname>
<listenerclassname>chat\system\event\listener\HourlyCleanUpCronjobExecuteChatCleanUpListener</listenerclassname> <listenerclassname>chat\system\event\listener\HourlyCleanUpCronjobExecuteChatCleanUpListener</listenerclassname>
<environment>user</environment> <environment>user</environment>
</eventlistener> </eventlistener>
<eventlistener name="hourlyCleanUpAdmin"> <eventlistener>
<eventclassname>wcf\system\cronjob\HourlyCleanUpCronjob</eventclassname> <eventclassname>wcf\system\cronjob\HourlyCleanUpCronjob</eventclassname>
<eventname>execute</eventname> <eventname>execute</eventname>
<listenerclassname>chat\system\event\listener\HourlyCleanUpCronjobExecuteChatCleanUpListener</listenerclassname> <listenerclassname>chat\system\event\listener\HourlyCleanUpCronjobExecuteChatCleanUpListener</listenerclassname>
<environment>admin</environment> <environment>admin</environment>
</eventlistener> </eventlistener>
<!-- Temprooms -->
<eventlistener name="temproomHourlyCleanUpUser">
<eventclassname>wcf\system\cronjob\HourlyCleanUpCronjob</eventclassname>
<eventname>execute</eventname>
<listenerclassname>chat\system\event\listener\HourlyCleanUpCronjobExecuteTemproomListener</listenerclassname>
<environment>user</environment>
</eventlistener>
<eventlistener name="temproomHourlyCleanUpAdmin">
<eventclassname>wcf\system\cronjob\HourlyCleanUpCronjob</eventclassname>
<eventname>execute</eventname>
<listenerclassname>chat\system\event\listener\HourlyCleanUpCronjobExecuteTemproomListener</listenerclassname>
<environment>admin</environment>
</eventlistener>
<eventlistener name="temproomCanSee">
<eventclassname>chat\data\room\Room</eventclassname>
<eventname>canSee</eventname>
<listenerclassname>chat\system\event\listener\RoomCanSeeTemproomListener</listenerclassname>
<environment>user</environment>
</eventlistener>
<eventlistener name="temproomRoomList">
<eventclassname>chat\acp\page\RoomListPage</eventclassname>
<eventname>calculateNumberOfPages</eventname>
<listenerclassname>chat\system\event\listener\RoomListPageTemproomListener</listenerclassname>
<environment>admin</environment>
</eventlistener>
<eventlistener name="temproomRoomEdit">
<eventclassname>chat\acp\form\RoomEditForm</eventclassname>
<eventname>readParameters</eventname>
<listenerclassname>chat\system\event\listener\RoomEditFormTemproomListener</listenerclassname>
<environment>admin</environment>
</eventlistener>
<eventlistener name="temproomSuspensionList">
<eventclassname>chat\acp\page\SuspensionListPage</eventclassname>
<eventname>readData</eventname>
<listenerclassname>chat\system\event\listener\SuspensionListPageTemproomListener</listenerclassname>
<environment>admin</environment>
</eventlistener>
<!-- User Limit -->
<eventlistener name="userLimitCanJoin">
<eventclassname>chat\data\room\Room</eventclassname>
<eventname>canJoin</eventname>
<listenerclassname>chat\system\event\listener\RoomCanJoinUserLimitListener</listenerclassname>
<environment>user</environment>
</eventlistener>
<!-- Suspensions -->
<eventlistener name="suspensionCanJoin">
<eventclassname>chat\data\room\Room</eventclassname>
<eventname>canJoin</eventname>
<listenerclassname>chat\system\event\listener\RoomCanJoinBanListener</listenerclassname>
<environment>user</environment>
</eventlistener>
<eventlistener name="suspensionCanWritePublicly">
<eventclassname>chat\data\room\Room</eventclassname>
<eventname>canWritePublicly</eventname>
<listenerclassname>chat\system\event\listener\RoomCanWritePubliclyMuteListener</listenerclassname>
<environment>user</environment>
</eventlistener>
<eventlistener name="suspensionInfoCommand">
<eventclassname>chat\system\command\InfoCommand</eventclassname>
<eventname>execute</eventname>
<listenerclassname>chat\system\event\listener\InfoCommandSuspensionsListener</listenerclassname>
<environment>user</environment>
</eventlistener>
<eventlistener name="moderatorPermissions">
<eventclassname>chat\data\room\RoomAction</eventclassname>
<eventname>getUsers</eventname>
<listenerclassname>chat\system\event\listener\RoomActionGetUsersModeratorListener</listenerclassname>
<environment>user</environment>
</eventlistener>
</import> </import>
</data>
<delete>
<!-- Hourly Cleanup -->
<eventlistener>
<eventclassname>wcf\system\cronjob\HourlyCleanUpCronjob</eventclassname>
<eventname>execute</eventname>
<listenerclassname>chat\system\event\listener\HourlyCleanUpCronjobExecuteChatCleanUpListener</listenerclassname>
<environment>user</environment>
</eventlistener>
<eventlistener>
<eventclassname>wcf\system\cronjob\HourlyCleanUpCronjob</eventclassname>
<eventname>execute</eventname>
<listenerclassname>chat\system\event\listener\HourlyCleanUpCronjobExecuteChatCleanUpListener</listenerclassname>
<environment>admin</environment>
</eventlistener>
<!-- Temprooms -->
<eventlistener>
<eventclassname>wcf\system\cronjob\HourlyCleanUpCronjob</eventclassname>
<eventname>execute</eventname>
<listenerclassname>chat\system\event\listener\HourlyCleanUpCronjobExecuteTemproomListener</listenerclassname>
<environment>user</environment>
</eventlistener>
<eventlistener>
<eventclassname>wcf\system\cronjob\HourlyCleanUpCronjob</eventclassname>
<eventname>execute</eventname>
<listenerclassname>chat\system\event\listener\HourlyCleanUpCronjobExecuteTemproomListener</listenerclassname>
<environment>admin</environment>
</eventlistener>
<eventlistener>
<eventclassname>chat\data\room\Room</eventclassname>
<eventname>canSee</eventname>
<listenerclassname>chat\system\event\listener\RoomCanSeeTemproomListener</listenerclassname>
<environment>user</environment>
</eventlistener>
<eventlistener>
<eventclassname>chat\acp\page\RoomListPage</eventclassname>
<eventname>calculateNumberOfPages</eventname>
<listenerclassname>chat\system\event\listener\RoomListPageTemproomListener</listenerclassname>
<environment>admin</environment>
</eventlistener>
<eventlistener>
<eventclassname>chat\acp\form\RoomEditForm</eventclassname>
<eventname>readParameters</eventname>
<listenerclassname>chat\system\event\listener\RoomEditFormTemproomListener</listenerclassname>
<environment>admin</environment>
</eventlistener>
<!-- User Limit -->
<eventlistener>
<eventclassname>chat\data\room\Room</eventclassname>
<eventname>canJoin</eventname>
<listenerclassname>chat\system\event\listener\RoomCanJoinUserLimitListener</listenerclassname>
<environment>user</environment>
</eventlistener>
<!-- Suspensions -->
<eventlistener>
<eventclassname>chat\data\room\Room</eventclassname>
<eventname>canJoin</eventname>
<listenerclassname>chat\system\event\listener\RoomCanJoinBanListener</listenerclassname>
<environment>user</environment>
</eventlistener>
<eventlistener>
<eventclassname>chat\data\room\Room</eventclassname>
<eventname>canWritePublicly</eventname>
<listenerclassname>chat\system\event\listener\RoomCanWritePubliclyMuteListener</listenerclassname>
<environment>user</environment>
</eventlistener>
<eventlistener>
<eventclassname>chat\system\command\InfoCommand</eventclassname>
<eventname>execute</eventname>
<listenerclassname>chat\system\event\listener\InfoCommandSuspensionsListener</listenerclassname>
<environment>user</environment>
</eventlistener>
</delete>
</data>

View File

@ -0,0 +1,61 @@
<?php
namespace be\bastelstu\chat;
/**
* Handles installation of Tims Chat.
*
* @author Tim Düsterhus
* @copyright 2010-2014 Tim Düsterhus
* @license Creative Commons Attribution-NonCommercial-ShareAlike <http://creativecommons.org/licenses/by-nc-sa/3.0/legalcode>
* @package be.bastelstu.chat
*/
// @codingStandardsIgnoreFile
final class Install {
/**
* Contains all the styles the current installation has.
*
* @var array<\wcf\data\style\Style>
*/
private $styles = null;
/**
* Do we need to update the page title?
*
* @var boolean
*/
private $updateTitle = false;
public function __construct() {
$this->styles = \wcf\system\style\StyleHandler::getInstance()->getAvailableStyles();
if (!defined('PAGE_TITLE') || !PAGE_TITLE) $this->updateTitle = true;
}
/**
* Resets styles.
*/
public function execute() {
foreach ($this->styles as $style) {
\wcf\system\style\StyleHandler::getInstance()->resetStylesheet($style);
}
if ($this->updateTitle) {
$sql = "UPDATE
wcf".WCF_N."_option
SET
optionValue = ?
WHERE
optionName = ?";
$stmt = \wcf\system\WCF::getDB()->prepareStatement($sql);
$stmt->execute(array('Tims Chat 3', 'page_title'));
\wcf\data\option\OptionEditor::resetCache();
}
\wcf\system\dashboard\DashboardHandler::setDefaultValues('com.woltlab.wcf.user.DashboardPage', array(
// content
'be.bastelstu.chat.onlineList' => 1
));
}
}
$install = new Install();
$install->execute();

View File

@ -0,0 +1,57 @@
<?php
namespace be\bastelstu\chat;
/**
* Handles updates of Tims Chat.
*
* @author Tim Düsterhus
* @copyright 2010-2014 Tim Düsterhus
* @license Creative Commons Attribution-NonCommercial-ShareAlike <http://creativecommons.org/licenses/by-nc-sa/3.0/legalcode>
* @package be.bastelstu.chat
*/
// @codingStandardsIgnoreFile
final class Update {
/**
* Contains all the rooms the current installation has.
*
* @var array<\wcf\data\chat\room\ChatRoom>
*/
private $rooms = null;
/**
* Contains all the styles the current installation has.
*
* @var array<\wcf\data\style\Style>
*/
private $styles = null;
public function __construct() {
$this->rooms = \chat\data\room\RoomCache::getInstance()->getRooms();
$this->styles = \wcf\system\style\StyleHandler::getInstance()->getAvailableStyles();
}
/**
* Notifies users to refresh the chat as the JS may no longer be fully compatible with the PHP code.
* Resets styles.
*/
public function execute() {
foreach ($this->rooms as $room) {
$messageAction = new \chat\data\message\MessageAction(array(), 'create', array(
'data' => array(
'roomID' => $room->roomID,
'time' => TIME_NOW,
'type' => \chat\data\message\Message::TYPE_INFORMATION,
'message' => \wcf\system\WCF::getLanguage()->get('chat.global.information.chatUpdate')
)
));
$messageAction->executeAction();
}
foreach ($this->styles as $style) {
\wcf\system\style\StyleHandler::getInstance()->resetStylesheet($style);
}
}
}
$update = new Update();
$update->execute();

15
file/acp/global.php Normal file
View File

@ -0,0 +1,15 @@
<?php
/**
* @author Tim Düsterhus
* @copyright 2010-2014 Tim Düsterhus
* @license Creative Commons Attribution-NonCommercial-ShareAlike <http://creativecommons.org/licenses/by-nc-sa/3.0/legalcode>
* @package be.bastelstu.chat
*/
// define paths
define('RELATIVE_CHAT_DIR', '../');
// include config
require_once(dirname(dirname(__FILE__)).'/config.inc.php');
// include WCF
require_once(RELATIVE_WCF_DIR.'acp/global.php');

9
file/acp/index.php Normal file
View File

@ -0,0 +1,9 @@
<?php
/**
* @author Tim Düsterhus
* @copyright 2010-2014 Tim Düsterhus
* @license Creative Commons Attribution-NonCommercial-ShareAlike <http://creativecommons.org/licenses/by-nc-sa/3.0/legalcode>
* @package be.bastelstu.chat
*/
require_once(__DIR__.'/global.php');
wcf\system\request\RequestHandler::getInstance()->handle('chat', true);

View File

@ -0,0 +1,92 @@
Tims Chat 3
===========
This is the javascript file providing functions related to the message log for [##Tims Chat##](https://github.com/wbbaddons/Tims-Chat).
### Copyright Information
# @author Maximilian Mader
# @copyright 2010-2014 Tim Düsterhus
# @license Creative Commons Attribution-NonCommercial-ShareAlike <http://creativecommons.org/licenses/by-nc-sa/3.0/legalcode>
# @package be.bastelstu.chat
###
## Code
(($, window) ->
"use strict";
_messageLogContent = null
_hasContent = {}
_proxy = null
init = ->
_messageLogContent = $ '#messageLogContent'
activeMenuItem = _messageLogContent.data 'active'
enableProxy = false
_messageLogContent.find('div.tabMenuContent > .subTabMenuContent').each (index, container) ->
containerID = $(container).wcfIdentify()
unless $("##{containerID}").hasClass 'empty'
_hasContent[containerID] = true
else
_hasContent[containerID] = false
enableProxy = true
if enableProxy
_proxy = new WCF.Action.Proxy
success: _success
_messageLogContent.bind 'wcftabsbeforeactivate',
_loadContent
if not _hasContent[activeMenuItem]
_loadContent {},
newPanel: $("##{activeMenuItem}")
newTab: $("##{activeMenuItem}").parent().find(".menu > ul > li").first()
_loadContent = (event, ui) ->
containerID = $(ui.newPanel).attr 'id'
if $("##{$(ui.newPanel).attr('id')}").hasClass 'tabMenuContainer'
containerID = $("##{containerID} > .subTabMenuContent").first().attr 'id'
tab = $("##{containerID}").parent().find(".menu > ul > li").first()
else
tab = $ ui.newTab
unless _hasContent[containerID]
start = _messageLogContent.data('baseTime') + (tab.data('hour') * 3600) + (tab.data('minutes') * 60)
_proxy.setOption 'data',
actionName: 'getMessages'
className: 'chat\\data\\message\\MessageAction'
parameters:
containerID: containerID
start: start
end: start + 1799
roomID: _messageLogContent.data 'roomID'
do _proxy.sendRequest
_success = (data, textStatus, jqWHR) ->
containerID = data.returnValues.containerID
_hasContent[containerID] = true
content = _messageLogContent.find "##{containerID}"
unless data.returnValues.template is ''
$("<div>#{data.returnValues.template}</div>").hide().appendTo content
unless data.returnValues.noMessages
content.addClass 'tabularBox'
do content.children().first().show
Log =
TabMenu:
init: init
window.be ?= {}
be.bastelstu ?= {}
be.bastelstu.Chat ?= {}
be.bastelstu.Chat.ACP ?= {}
be.bastelstu.Chat.ACP.Log ?= {}
window.be.bastelstu.Chat.ACP.Log = Log
)(jQuery, @)

13
file/global.php Normal file
View File

@ -0,0 +1,13 @@
<?php
/**
* @author Tim Düsterhus
* @copyright 2010-2014 Tim Düsterhus
* @license Creative Commons Attribution-NonCommercial-ShareAlike <http://creativecommons.org/licenses/by-nc-sa/3.0/legalcode>
* @package be.bastelstu.chat
*/
// include config
require_once(__DIR__.'/config.inc.php');
// include wcf
require_once(RELATIVE_WCF_DIR.'global.php');

BIN
file/images/chatLogo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

10
file/index.php Normal file
View File

@ -0,0 +1,10 @@
<?php
/**
* @author Tim Düsterhus
* @copyright 2010-2014 Tim Düsterhus
* @license Creative Commons Attribution-NonCommercial-ShareAlike <http://creativecommons.org/licenses/by-nc-sa/3.0/legalcode>
* @package be.bastelstu.chat
*/
require_once(__DIR__.'/global.php');
\wcf\system\request\RequestHandler::getInstance()->handle('chat');

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,150 @@
<?php
namespace chat\acp\form;
use \wcf\system\exception\UserInputException;
use \wcf\system\language\I18nHandler;
use \wcf\system\WCF;
/**
* Shows the chatroom add form.
*
* @author Tim Düsterhus
* @copyright 2010-2014 Tim Düsterhus
* @license Creative Commons Attribution-NonCommercial-ShareAlike <http://creativecommons.org/licenses/by-nc-sa/3.0/legalcode>
* @package be.bastelstu.chat
* @subpackage acp.form
*/
class RoomAddForm extends \wcf\form\AbstractForm {
/**
* @see \wcf\acp\page\AbstractPage::$activeMenuItem
*/
public $activeMenuItem = 'chat.acp.menu.link.room.add';
/**
* @see \wcf\page\AbstractPage::$neededPermissions
*/
public $neededPermissions = array('admin.chat.canAddRoom');
/**
* Title of the room
*
* @var string
*/
public $title = '';
/**
* Topic of the room
*
* @var string
*/
public $topic = '';
/**
* @see \wcf\page\AbstractPage::__construct()
*/
public function __run() {
$this->objectTypeID = \wcf\system\acl\ACLHandler::getInstance()->getObjectTypeID('be.bastelstu.chat.room');
parent::__run();
}
/**
* @see \wcf\page\IPage::readParameters()
*/
public function readParameters() {
parent::readParameters();
I18nHandler::getInstance()->register('title');
I18nHandler::getInstance()->register('topic');
}
/**
* @see \wcf\form\IForm::readFormParameters()
*/
public function readFormParameters() {
parent::readFormParameters();
I18nHandler::getInstance()->readValues();
if (I18nHandler::getInstance()->isPlainValue('title')) $this->title = I18nHandler::getInstance()->getValue('title');
if (I18nHandler::getInstance()->isPlainValue('topic')) $this->topic = I18nHandler::getInstance()->getValue('topic');
}
/**
* @see \wcf\form\IForm::validate()
*/
public function validate() {
parent::validate();
// validate title
if (!I18nHandler::getInstance()->validateValue('title')) {
throw new UserInputException('title');
}
}
/**
* @see \wcf\form\IForm::save()
*/
public function save() {
parent::save();
// save room
$this->objectAction = new \chat\data\room\RoomAction(array(), 'create', array('data' => array_merge($this->additionalFields, array(
'title' => $this->title,
'topic' => $this->topic
))));
$this->objectAction->executeAction();
$returnValues = $this->objectAction->getReturnValues();
$roomEditor = new \chat\data\room\RoomEditor($returnValues['returnValues']);
$roomID = $returnValues['returnValues']->roomID;
if (!I18nHandler::getInstance()->isPlainValue('title')) {
I18nHandler::getInstance()->save('title', 'chat.room.title'.$roomID, 'chat.room', \chat\util\ChatUtil::getPackageID());
// update title
$roomEditor->update(array(
'title' => 'chat.room.title'.$roomID
));
}
if (!I18nHandler::getInstance()->isPlainValue('topic')) {
I18nHandler::getInstance()->save('topic', 'chat.room.topic'.$roomID, 'chat.room', \chat\util\ChatUtil::getPackageID());
// update topic
$roomEditor->update(array(
'topic' => 'chat.room.topic'.$roomID
));
}
\wcf\system\acl\ACLHandler::getInstance()->save($roomID, $this->objectTypeID);
\wcf\system\acl\ACLHandler::getInstance()->disableAssignVariables();
\chat\system\permission\PermissionHandler::clearCache();
$this->saved();
// reset values
$this->topic = $this->title = '';
I18nHandler::getInstance()->reset();
// show success
WCF::getTPL()->assign(array(
'success' => true
));
}
/**
* @see \wcf\page\IPage::assignVariables()
*/
public function assignVariables() {
parent::assignVariables();
I18nHandler::getInstance()->assignVariables();
\wcf\system\acl\ACLHandler::getInstance()->assignVariables($this->objectTypeID);
WCF::getTPL()->assign(array(
'action' => 'add',
'title' => $this->title,
'topic' => $this->topic,
'objectTypeID' => $this->objectTypeID
));
}
}

View File

@ -0,0 +1,132 @@
<?php
namespace chat\acp\form;
use \wcf\system\language\I18nHandler;
use \wcf\system\WCF;
/**
* Shows the chatroom edit form.
*
* @author Tim Düsterhus
* @copyright 2010-2014 Tim Düsterhus
* @license Creative Commons Attribution-NonCommercial-ShareAlike <http://creativecommons.org/licenses/by-nc-sa/3.0/legalcode>
* @package be.bastelstu.chat
* @subpackage acp.form
*/
class RoomEditForm extends RoomAddForm {
/**
* @see \wcf\page\AbstractPage::$templateName
*/
public $templateName = 'roomAdd';
/**
* @see \wcf\acp\form\ACPForm::$activeMenuItem
*/
public $activeMenuItem = 'chat.acp.menu.link.chat';
/**
* @see \wcf\page\AbstractPage::$neededPermissions
*/
public $neededPermissions = array('admin.chat.canEditRoom');
/**
* room id
*
* @var integer
*/
public $roomID = 0;
/**
* room object
*
* @var \chat\data\room\Room
*/
public $roomObj = null;
/**
* @see \wcf\page\IPage::readParameters()
*/
public function readParameters() {
parent::readParameters();
if (isset($_REQUEST['id'])) $this->roomID = intval($_REQUEST['id']);
$this->roomObj = new \chat\data\room\Room($this->roomID);
if (!$this->roomObj->roomID) {
throw new \wcf\system\exception\IllegalLinkException();
}
if (!$this->roomObj->permanent) {
throw new \wcf\system\exception\PermissionDeniedException();
}
}
/**
* @see \wcf\form\IForm::save()
*/
public function save() {
\wcf\form\AbstractForm::save();
$this->title = 'chat.room.title'.$this->roomObj->roomID;
if (I18nHandler::getInstance()->isPlainValue('title')) {
I18nHandler::getInstance()->remove($this->title);
$this->title = I18nHandler::getInstance()->getValue('title');
}
else {
I18nHandler::getInstance()->save('title', $this->title, 'chat.room', \chat\util\ChatUtil::getPackageID());
}
$this->topic = 'chat.room.topic'.$this->roomObj->roomID;
if (I18nHandler::getInstance()->isPlainValue('topic')) {
I18nHandler::getInstance()->remove($this->topic);
$this->topic = I18nHandler::getInstance()->getValue('topic');
}
else {
I18nHandler::getInstance()->save('topic', $this->topic, 'chat.room', \chat\util\ChatUtil::getPackageID());
}
\wcf\system\acl\ACLHandler::getInstance()->save($this->roomID, $this->objectTypeID);
\wcf\system\acl\ACLHandler::getInstance()->disableAssignVariables();
\chat\system\permission\PermissionHandler::clearCache();
// update room
$this->objectAction = new \chat\data\room\RoomAction(array($this->roomID), 'update', array('data' => array_merge($this->additionalFields, array(
'title' => $this->title,
'topic' => $this->topic
))));
$this->objectAction->executeAction();
$this->saved();
// show success
WCF::getTPL()->assign(array(
'success' => true
));
}
/**
* @see \wcf\page\IPage::readData()
*/
public function readData() {
parent::readData();
if (!count($_POST)) {
I18nHandler::getInstance()->setOptions('title', \chat\util\ChatUtil::getPackageID(), $this->roomObj->title, 'chat.room.title\d+');
I18nHandler::getInstance()->setOptions('topic', \chat\util\ChatUtil::getPackageID(), $this->roomObj->topic, 'chat.room.topic\d+');
$this->title = $this->roomObj->title;
$this->topic = $this->roomObj->topic;
}
}
/**
* @see \wcf\page\IPage::assignVariables()
*/
public function assignVariables() {
parent::assignVariables();
I18nHandler::getInstance()->assignVariables((bool) count($_POST));
WCF::getTPL()->assign(array(
'roomID' => $this->roomID,
'action' => 'edit'
));
}
}

View File

@ -0,0 +1,183 @@
<?php
namespace chat\acp\page;
use \wcf\system\WCF;
/**
* Lists chat suspensions.
*
* @author Maximilian Mader
* @copyright 2010-2014 Tim Düsterhus
* @license Creative Commons Attribution-NonCommercial-ShareAlike <http://creativecommons.org/licenses/by-nc-sa/3.0/legalcode>
* @package be.bastelstu.chat
* @subpackage acp.page
*/
class ChatSuspensionListPage extends \wcf\page\SortablePage {
/**
* @see \wcf\page\AbstractPage::$activeMenuItem
*/
public $activeMenuItem = 'chat.acp.menu.link.suspension.list';
/**
* @see \wcf\page\AbstractPage::$neededPermissions
*/
public $neededPermissions = array('admin.chat.canManageSuspensions');
/**
* @see \wcf\page\SortablePage::$defaultSortField
*/
public $defaultSortField = 'expires';
/**
* @see \wcf\page\SortablePage::$validSortFields
*/
public $validSortFields = array('suspensionID', 'userID', 'username', 'roomID', 'type', 'expires', 'issuer', 'time', 'reason');
/**
* @see \wcf\page\MultipleLinkPage::$objectListClassName
*/
public $objectListClassName = 'chat\data\suspension\SuspensionList';
/**
* type filter
*
* @var integer
*/
public $filterSuspensionType = null;
/**
* user filter
*
* @var integer
*/
public $filterUserID = null;
/*
* username
*
* @var String
*/
public $filterUsername = null;
/**
* issuer filter
*
* @var integer
*/
public $filterIssuerUserID = null;
/*
* issuer username
*
* @var String
*/
public $filterIssuerUsername = null;
/**
* room filter
*
* @var integer
*/
public $filterRoomID = null;
/**
* display revoked suspensions
*
* @var integer
*/
public $displayRevoked = 0;
/**
* @see \wcf\page\IPage::readParameters()
*/
public function readParameters() {
parent::readParameters();
// get usernames
if (isset($_REQUEST['username']) && !empty($_REQUEST['username'])) $this->filterUsername = \wcf\util\StringUtil::trim($_REQUEST['username']);
if (isset($_REQUEST['issuerUsername']) && !empty($_REQUEST['issuerUsername'])) $this->filterIssuerUsername = \wcf\util\StringUtil::trim($_REQUEST['issuerUsername']);
// get user IDs by username
if ($this->filterUsername != null) $this->filterUserID = \wcf\data\user\UserProfile::getUserProfileByUsername($this->filterUsername)->userID;
if ($this->filterIssuerUsername != null) $this->filterIssuerUserID = \wcf\data\user\UserProfile::getUserProfileByUsername($this->filterIssuerUsername)->userID;
// get user IDs by request if no username was sent
if ($this->filterUserID === null && isset($_REQUEST['userID']) && !empty($_REQUEST['userID'])) $this->filterUserID = intval($_REQUEST['userID']);
if ($this->filterIssuerUserID === null && isset($_REQUEST['issuerUserID']) && !empty($_REQUEST['issuerUserID'])) $this->filterIssuerUserID = intval($_REQUEST['issuerUserID']);
// get usernames by ID if no usernames were sent
if ($this->filterUsername === null) $this->filterUsername = \wcf\data\user\UserProfile::getUserProfile($this->filterUserID);
if ($this->filterIssuerUsername === null) $this->filterIssuerUsername = \wcf\data\user\UserProfile::getUserProfile($this->filterIssuerUserID);
// get room IDs by request
if (isset($_REQUEST['roomID']) && $_REQUEST['roomID'] != -1) {
$this->filterRoomID = intval($_REQUEST['roomID']);
$room = \chat\data\room\RoomCache::getInstance()->getRoom($this->filterRoomID);
if (!$room) throw new \wcf\system\exception\IllegalLinkException();
if (!$room->permanent) throw new \wcf\system\exception\PermissionDeniedException();
}
if (isset($_REQUEST['suspensionType']) && !empty($_REQUEST['suspensionType'])) $this->filterSuspensionType = \wcf\util\StringUtil::trim($_REQUEST['suspensionType']);
// display revoked
if (isset($_REQUEST['displayRevoked'])) $this->displayRevoked = intval($_REQUEST['displayRevoked']);
}
/**
* @see wcf\page\IPage::assignVariables()
*/
public function assignVariables() {
parent::assignVariables();
$rooms = \chat\data\room\RoomCache::getInstance()->getRooms();
foreach ($rooms as $id => $room) {
if (!$room->permanent) unset($rooms[$id]);
}
WCF::getTPL()->assign(array(
'availableRooms' => $rooms,
'roomID' => ($this->filterRoomID !== null) ? $this->filterRoomID : -1,
'username' => $this->filterUsername,
'issuerUsername' => $this->filterIssuerUsername,
'suspensionType' => $this->filterSuspensionType,
'userID' => $this->filterUserID,
'issuerUserID' => $this->filterIssuerUserID,
'displayRevoked' => $this->displayRevoked
));
}
/**
* @see \wcf\page\MultipleLinkPage::readObjects()
*/
protected function initObjectList() {
parent::initObjectList();
$this->objectList->sqlSelects .= "user_table_muted.username, user_table_issuer.username AS issuerUsername, user_table_revoker.username AS revokerUsername, room_table.title AS roomTitle";
$this->objectList->sqlJoins .= "
LEFT JOIN wcf".WCF_N."_user user_table_muted
ON suspension.userID = user_table_muted.userID
LEFT JOIN wcf".WCF_N."_user user_table_issuer
ON suspension.issuer = user_table_issuer.userID
LEFT JOIN wcf".WCF_N."_user user_table_revoker
ON suspension.revoker = user_table_revoker.userID";
$conditionJoins = " LEFT JOIN chat".WCF_N."_room room_table
ON suspension.roomID = room_table.roomID";
$this->objectList->sqlConditionJoins .= $conditionJoins;
$this->objectList->sqlJoins .= $conditionJoins;
if (!$this->displayRevoked) {
$this->objectList->getConditionBuilder()->add('expires > ?', array(TIME_NOW));
}
$this->objectList->getConditionBuilder()->add('(room_table.permanent = ? OR suspension.roomID IS NULL)', array(1));
if ($this->filterSuspensionType !== null) $this->objectList->getConditionBuilder()->add('suspension.type = ?', array($this->filterSuspensionType));
if ($this->filterUserID !== null) $this->objectList->getConditionBuilder()->add('suspension.userID = ?', array($this->filterUserID));
if ($this->filterIssuerUserID !== null) $this->objectList->getConditionBuilder()->add('suspension.issuer = ?', array($this->filterIssuerUserID));
if ($this->filterRoomID !== null) {
if ($this->filterRoomID === 0) {
$this->objectList->getConditionBuilder()->add('suspension.roomID IS NULL', array());
}
else {
$this->objectList->getConditionBuilder()->add('suspension.roomID = ?', array($this->filterRoomID));
}
}
}
}

View File

@ -0,0 +1,141 @@
<?php
namespace chat\acp\page;
use \wcf\system\WCF;
/**
* Handles text/plain download of chat log.
*
* @author Maximilian Mader
* @copyright 2010-2014 Tim Düsterhus
* @license Creative Commons Attribution-NonCommercial-ShareAlike <http://creativecommons.org/licenses/by-nc-sa/3.0/legalcode>
* @package be.bastelstu.chat
* @subpackage acp.page
*/
class MessageLogDownloadPage extends \wcf\page\AbstractPage {
/**
* @see \wcf\page\AbstractPage::$activeMenuItem
*/
public $activeMenuItem = 'chat.acp.menu.link.log';
/**
* @see \wcf\page\AbstractPage::$neededPermissions
*/
public $neededPermissions = array(
'admin.chat.canReadLog'
);
public $useTemplate = false;
/**
* messages for the given day
*
* @var array<\chat\data\message\Message>
*/
public $messages = array();
/**
* given roomID
*
* @var integer
*/
public $roomID = 0;
/**
* given date
*
* @var \DateTime
*/
public $date = null;
/**
* active room
*
* @var \chat\data\room\Room
*/
public $room = null;
/**
* available rooms
*
* @var array<\chat\data\room\Room>
*/
public $rooms = array();
public $tmpFile = '';
/**
* @see \wcf\page\IPage::readData()
*/
public function readData() {
parent::readData();
$now = new \DateTime('now', WCF::getUser()->getTimeZone());
if ($this->date->getTimestamp() > $now->getTimestamp()) {
throw new \wcf\system\exception\IllegalLinkException();
}
$oldest = new \DateTime('today -'.ceil(CHAT_LOG_ARCHIVETIME / 1440).'day', WCF::getUser()->getTimeZone());
if (CHAT_LOG_ARCHIVETIME !== -1 && $this->date->getTimestamp() < $oldest->getTimestamp()) {
throw new \wcf\system\exception\IllegalLinkException();
}
$this->tmpFile = \wcf\util\FileUtil::getTemporaryFilename();
touch($this->tmpFile);
\wcf\util\FileUtil::makeWritable($this->tmpFile);
if (is_writable($this->tmpFile)) {
$file = new \wcf\system\io\File($this->tmpFile);
$file->write(WCF::getLanguage()->get('chat.acp.log.title') . ': ' . (string) $this->room . "\n");
for ($start = $this->date->getTimestamp(), $end = $this->date->add(\DateInterval::createFromDateString('1day'))->sub(\DateInterval::createFromDateString('1second'))->getTimestamp(); $start < $end; $start += 1800) {
$file->write(WCF::getTpl()->fetch('messageLogDownload', 'chat', array('messages' => \chat\data\message\MessageList::getMessagesBetween($this->room, $start, $start + 1799))));
}
$file->close();
}
else {
throw new \wcf\system\exception\IllegalLinkException();
}
}
/**
* @see \wcf\page\IPage::readParameters()
*/
public function readParameters() {
parent::readParameters();
if (isset($_REQUEST['id'])) {
$this->roomID = intval($_REQUEST['id']);
}
else {
throw new \wcf\system\exception\IllegalLinkException();
}
$this->room = \chat\data\room\RoomCache::getInstance()->getRoom($this->roomID);
if (!$this->room) throw new \wcf\system\exception\IllegalLinkException();
if (!$this->room->permanent) throw new \wcf\system\exception\PermissionDeniedException();
if (isset($_REQUEST['date'])) $date = $_REQUEST['date'].' 00:00:00';
else $date = 'today 00:00:00';
try {
$this->date = new \DateTime($date, WCF::getUser()->getTimeZone());
}
catch (\Exception $e) {
throw new \wcf\system\exception\IllegalLinkException();
}
}
/**
* @see wcf\page\IPage::show()
*/
public function show() {
parent::show();
$fileReader = new \wcf\util\FileReader($this->tmpFile, array('mimeType' => 'text/plain', 'filename' => str_replace(' ', '-', WCF::getLanguage()->get('chat.acp.log.title') . ' ' . $this->room.'-'.\wcf\util\DateUtil::format($this->date, 'Y-m-d').'.txt')));
$fileReader->send();
exit;
}
}

View File

@ -0,0 +1,130 @@
<?php
namespace chat\acp\page;
use \wcf\system\WCF;
/**
* Lists chat log.
*
* @author Maximilian Mader
* @copyright 2010-2014 Tim Düsterhus
* @license Creative Commons Attribution-NonCommercial-ShareAlike <http://creativecommons.org/licenses/by-nc-sa/3.0/legalcode>
* @package be.bastelstu.chat
* @subpackage acp.page
*/
class MessageLogPage extends \wcf\page\AbstractPage {
/**
* @see \wcf\page\AbstractPage::$activeMenuItem
*/
public $activeMenuItem = 'chat.acp.menu.link.log';
/**
* @see \wcf\page\AbstractPage::$neededPermissions
*/
public $neededPermissions = array(
'admin.chat.canReadLog'
);
/**
* name of error field
* @var string
*/
public $errorField = '';
/**
* error type
* @var string
*/
public $errorType = '';
/**
* given roomID
*
* @var integer
*/
public $roomID = 0;
/**
* given date
*
* @var integer
*/
public $date = 0;
/**
* active room
*
* @var \chat\data\room\Room
*/
public $room = null;
/**
* available rooms
*
* @var array<\chat\data\room\Room>
*/
public $rooms = array();
/**
* @see \wcf\page\IPage::readData()
*/
public function readData() {
parent::readData();
try {
if ($this->date > TIME_NOW) {
throw new \wcf\system\exception\UserInputException('date', 'inFuture');
}
if (CHAT_LOG_ARCHIVETIME !== -1 && $this->date < strtotime('today 00:00:00 -'.ceil(CHAT_LOG_ARCHIVETIME / 1440).'day')) {
throw new \wcf\system\exception\UserInputException('date', 'tooLongAgo');
}
}
catch (\wcf\system\exception\UserInputException $e) {
$this->errorField = $e->getField();
$this->errorType = $e->getType();
return;
}
}
/**
* @see \wcf\page\IPage::readParameters()
*/
public function readParameters() {
parent::readParameters();
$this->rooms = \chat\data\room\RoomCache::getInstance()->getRooms();
foreach ($this->rooms as $id => $room) {
if (!$room->permanent) unset($this->rooms[$id]);
}
if (isset($_REQUEST['id'])) $this->roomID = intval($_REQUEST['id']);
else $this->roomID = reset($this->rooms)->roomID;
$this->room = \chat\data\room\RoomCache::getInstance()->getRoom($this->roomID);
if (!$this->room) throw new \wcf\system\exception\IllegalLinkException();
if (!$this->room->permanent) throw new \wcf\system\exception\PermissionDeniedException();
if (isset($_REQUEST['date'])) $date = $_REQUEST['date'].' 00:00:00';
else $date = 'today 00:00:00';
$this->date = @strtotime($date);
if ($this->date === false) throw new \wcf\system\exception\IllegalLinkException();
}
/**
* @see wcf\page\IPage::assignVariables()
*/
public function assignVariables() {
parent::assignVariables();
WCF::getTPL()->assign(array(
'rooms' => $this->rooms,
'room' => $this->room,
'date' => $this->date,
'errorField' => $this->errorField,
'errorType' => $this->errorType
));
}
}

View File

@ -0,0 +1,56 @@
<?php
namespace chat\acp\page;
use \wcf\system\WCF;
/**
* Lists available chatrooms.
*
* @author Tim Düsterhus
* @copyright 2010-2014 Tim Düsterhus
* @license Creative Commons Attribution-NonCommercial-ShareAlike <http://creativecommons.org/licenses/by-nc-sa/3.0/legalcode>
* @package be.bastelstu.chat
* @subpackage acp.page
*/
class RoomListPage extends \wcf\page\AbstractPage {
/**
* @see \wcf\page\AbstractPage::$activeMenuItem
*/
public $activeMenuItem = 'chat.acp.menu.link.room.list';
/**
* @see \wcf\page\AbstractPage::$neededPermissions
*/
public $neededPermissions = array(
'admin.chat.canEditRoom',
'admin.chat.canDeleteRoom'
);
/**
* room list
* @var \chat\data\room\RoomListPage
*/
public $objects = null;
/**
* @see \wcf\page\IPage::readData()
*/
public function readData() {
parent::readData();
$this->objects = new \chat\data\room\RoomList();
$this->objects->sqlOrderBy = 'showOrder ASC';
$this->objects->getConditionBuilder()->add('permanent = ?', array(1));
$this->objects->readObjects();
}
/**
* @see wcf\page\IPage::assignVariables()
*/
public function assignVariables() {
parent::assignVariables();
WCF::getTPL()->assign(array(
'objects' => $this->objects
));
}
}

View File

@ -0,0 +1,20 @@
<?php
namespace chat\data;
/**
* Basic implementation that sets proper table name.
*
* @author Tim Düsterhus
* @copyright 2010-2014 Tim Düsterhus
* @license Creative Commons Attribution-NonCommercial-ShareAlike <http://creativecommons.org/licenses/by-nc-sa/3.0/legalcode>
* @package be.bastelstu.chat
* @subpackage data
*/
abstract class CHATDatabaseObject extends \wcf\data\DatabaseObject {
/**
* @see \wcf\data\DatabaseObject::getDatabaseTableName()
*/
public static function getDatabaseTableName() {
return 'chat'.WCF_N.'_'.static::$databaseTableName;
}
}

View File

@ -0,0 +1,170 @@
<?php
namespace chat\data\message;
use \chat\util\ChatUtil;
use \wcf\system\bbcode\AttachmentBBCode;
use \wcf\system\Regex;
use \wcf\system\WCF;
/**
* Represents a chat message.
*
* @author Tim Düsterhus
* @copyright 2010-2014 Tim Düsterhus
* @license Creative Commons Attribution-NonCommercial-ShareAlike <http://creativecommons.org/licenses/by-nc-sa/3.0/legalcode>
* @package be.bastelstu.chat
* @subpackage data.message
*/
class Message extends \chat\data\CHATDatabaseObject {
/**
* @see \wcf\data\DatabaseObject::$databaseTableName
*/
protected static $databaseTableName = 'message';
/**
* @see \wcf\data\DatabaseObject::$databaseTableIndexName
*/
protected static $databaseTableIndexName = 'messageID';
const TYPE_NORMAL = 0;
const TYPE_JOIN = 1;
const TYPE_LEAVE = 2;
const TYPE_AWAY = 3;
const TYPE_BACK = 4;
const TYPE_MODERATE = 5;
const TYPE_ME = 6;
const TYPE_WHISPER = 7;
const TYPE_INFORMATION = 8;
const TYPE_CLEAR = 9;
const TYPE_TEAM = 10;
const TYPE_GLOBALMESSAGE = 11;
const TYPE_ATTACHMENT = 12;
/**
* cache for users
* @var array<\wcf\data\user\User>
*/
protected static $users = array();
/**
* @see \wcf\data\DatabaseObject::handleData()
*/
protected function handleData($data) {
parent::handleData($data);
if ($this->data['additionalData'] !== null) {
$this->data['additionalData'] = unserialize($this->data['additionalData']);
}
}
/**
* @see \chat\data\message\Message::getFormattedMessage()
*/
public function __toString() {
return $this->getFormattedMessage();
}
/**
* Returns the formatted message.
*
* @param string $outputType outputtype for messageparser
* @return string
*/
public function getFormattedMessage($type = 'text/html') {
$message = $this->message;
$messageParser = \wcf\system\bbcode\MessageParser::getInstance();
$messageParser->setOutputType($type);
switch ($this->type) {
case self::TYPE_JOIN:
case self::TYPE_LEAVE:
case self::TYPE_BACK:
$message = WCF::getLanguage()->getDynamicVariable('chat.message.'.$this->type, $this->data['additionalData'] ?: array());
break;
case self::TYPE_AWAY:
$message = WCF::getLanguage()->getDynamicVariable('chat.message.'.$this->type, array('message' => $message));
break;
case self::TYPE_MODERATE:
$message = unserialize($message);
$message = WCF::getLanguage()->getDynamicVariable('chat.message.'.$this->type.'.'.$message['type'], $message ?: array());
if ($type === 'text/plain') return $message;
$message = $messageParser->parse($message, false, false, true, false);
break;
case self::TYPE_ATTACHMENT:
$attachmentList = new \wcf\data\attachment\GroupedAttachmentList('be.bastelstu.chat.message');
$attachmentList->getConditionBuilder()->add('attachment.objectID IN (?)', array($this->messageID));
$attachmentList->readObjects();
AttachmentBBCode::setAttachmentList($attachmentList);
AttachmentBBCode::setObjectID($this->messageID);
$message = $messageParser->parse($message, false, false, true, false);
break;
case self::TYPE_WHISPER:
case self::TYPE_NORMAL:
case self::TYPE_ME:
default:
if ($type === 'text/plain') return $message;
$message = $messageParser->parse($message, $this->enableSmilies, $this->enableHTML, true, false);
break;
}
return $message;
}
/**
* Returns the username.
*
* @param boolean $colored
* @return string
*/
public function getUsername($colored = false) {
$username = $this->username;
if ($this->type == self::TYPE_INFORMATION) return WCF::getLanguage()->get('chat.global.information');
if ($colored) {
$username = \chat\util\ChatUtil::gradient($username, $this->color1, $this->color2);
}
return $username;
}
/**
* Converts this message into json-form.
*
* @param boolean $raw
* @return string
*/
public function jsonify($raw = false) {
switch ($this->type) {
case self::TYPE_WHISPER:
case self::TYPE_NORMAL:
case self::TYPE_INFORMATION:
$separator = ':';
break;
default:
$separator = '';
break;
}
$array = array(
'formattedUsername' => $this->getUsername(true),
'formattedMessage' => $this->getFormattedMessage('text/html'),
'formattedTime' => \wcf\util\DateUtil::format(\wcf\util\DateUtil::getDateTimeByTimestamp($this->time), 'H:i:s'),
'separator' => $separator,
'message' => $this->getFormattedMessage('text/plain'),
'sender' => (int) $this->sender,
'username' => $this->getUsername(),
'time' => (int) $this->time,
'receiver' => (int) $this->receiver,
'type' => (int) $this->type,
'roomID' => (int) $this->roomID,
'messageID' => (int) $this->messageID,
'additionalData' => $this->additionalData
);
if ($raw) return $array;
return \wcf\util\JSON::encode($array);
}
}

View File

@ -0,0 +1,267 @@
<?php
namespace chat\data\message;
use chat\data\room;
use wcf\system\exception\UserInputException;
use wcf\system\WCF;
use wcf\util\MessageUtil;
/**
* Executes message related actions.
*
* @author Tim Düsterhus
* @copyright 2010-2014 Tim Düsterhus
* @license Creative Commons Attribution-NonCommercial-ShareAlike <http://creativecommons.org/licenses/by-nc-sa/3.0/legalcode>
* @package be.bastelstu.chat
* @subpackage chat.message
*/
class MessageAction extends \wcf\data\AbstractDatabaseObjectAction {
/**
* @see \wcf\data\AbstractDatabaseObjectAction::$className
*/
protected $className = '\chat\data\message\MessageEditor';
/**
* Removes old messages.
*
* @return integer Number of deleted messages.
*/
public function prune() {
if (CHAT_LOG_ARCHIVETIME == -1) return 0;
$sql = "SELECT
".call_user_func(array($this->className, 'getDatabaseTableIndexName'))."
FROM
".call_user_func(array($this->className, 'getDatabaseTableName'))."
WHERE
time < ?";
$stmt = \wcf\system\WCF::getDB()->prepareStatement($sql);
$stmt->execute(array(TIME_NOW - CHAT_LOG_ARCHIVETIME * 60));
$objectIDs = array();
while ($objectID = $stmt->fetchColumn()) $objectIDs[] = $objectID;
return call_user_func(array($this->className, 'deleteAll'), $objectIDs);
}
/**
* Validates message sending.
*/
public function validateSend() {
// read user data
$this->parameters['userData']['color1'] = WCF::getUser()->chatColor1;
$this->parameters['userData']['color2'] = WCF::getUser()->chatColor2;
$this->parameters['userData']['roomID'] = WCF::getUser()->chatRoomID;
$this->parameters['userData']['away'] = WCF::getUser()->chatAway;
// read and validate room
$this->parameters['room'] = room\RoomCache::getInstance()->getRoom($this->parameters['userData']['roomID']);
if ($this->parameters['room'] === null) throw new \wcf\system\exception\IllegalLinkException();
if (!$this->parameters['room']->canEnter()) throw new \wcf\system\exception\PermissionDeniedException();
if (!$this->parameters['room']->canWrite()) throw new \wcf\system\exception\PermissionDeniedException();
// read parameters
$this->readString('text');
$this->readBoolean('enableSmilies', true);
$this->parameters['text'] = MessageUtil::stripCrap($this->parameters['text']);
$this->parameters['enableHTML'] = false;
// validate text
if (mb_strlen($this->parameters['text']) > CHAT_MAX_LENGTH) throw new UserInputException('text', 'tooLong');
// search for disallowed bbcodes
$disallowedBBCodes = \wcf\system\bbcode\BBCodeParser::getInstance()->validateBBCodes($this->parameters['text'], explode(',', WCF::getSession()->getPermission('user.chat.allowedBBCodes')));
if (!empty($disallowedBBCodes)) {
throw new UserInputException('text', WCF::getLanguage()->getDynamicVariable('wcf.message.error.disallowedBBCodes', array('disallowedBBCodes' => $disallowedBBCodes)));
}
// search for censored words
if (ENABLE_CENSORSHIP) {
$result = \wcf\system\message\censorship\Censorship::getInstance()->test($this->parameters['text']);
if ($result) {
throw new UserInputException('message', WCF::getLanguage()->getDynamicVariable('wcf.message.error.censoredWordsFound', array('censoredWords' => $result)));
}
}
$editor = new \wcf\data\user\UserEditor(WCF::getUser());
$editor->update(array(
'chatAway' => null,
'chatLastActivity' => TIME_NOW
));
// mark user as back
if ($this->parameters['userData']['away'] !== null) {
$messageAction = new MessageAction(array(), 'create', array(
'data' => array(
'roomID' => $this->parameters['room']->roomID,
'sender' => WCF::getUser()->userID,
'username' => WCF::getUser()->username,
'time' => TIME_NOW,
'type' => Message::TYPE_BACK,
'message' => '',
'color1' => $this->parameters['userData']['color1'],
'color2' => $this->parameters['userData']['color2']
)
));
$messageAction->executeAction();
}
// handle commands
$commandHandler = new \chat\system\command\CommandHandler($this->parameters['text'], $this->parameters['room']);
if ($commandHandler->isCommand()) {
try {
$command = $commandHandler->loadCommand();
if ($command->enableSmilies != \chat\system\command\ICommand::SETTING_USER) $this->parameters['enableSmilies'] = $command->enableSmilies;
$this->parameters['enableHTML'] = $command->enableHTML;
$this->parameters['type'] = $command->getType();
$this->parameters['text'] = $command->getMessage();
$this->parameters['receiver'] = $command->getReceiver();
$this->parameters['additionalData'] = $command->getAdditionalData();
}
catch (\chat\system\command\NotFoundException $e) {
throw new UserInputException('text', WCF::getLanguage()->getDynamicVariable('chat.error.notFound', array('exception' => $e)));
}
catch (\chat\system\command\UserNotFoundException $e) {
throw new UserInputException('text', WCF::getLanguage()->getDynamicVariable('chat.error.userNotFound', array('exception' => $e)));
}
catch (\InvalidArgumentException $e) {
throw new UserInputException('text', WCF::getLanguage()->getDynamicVariable('chat.error.invalidArgument', array('exception' => $e)));
}
}
else {
$this->parameters['type'] = Message::TYPE_NORMAL;
$this->parameters['receiver'] = null;
$this->parameters['additionalData'] = null;
$this->parameters['text'] = \wcf\system\bbcode\PreParser::getInstance()->parse($this->parameters['text'], explode(',', WCF::getSession()->getPermission('user.chat.allowedBBCodes')));
}
}
/**
* Creates sent message.
*/
public function send() {
$this->objectAction = new MessageAction(array(), 'create', array(
'data' => array(
'roomID' => $this->parameters['room']->roomID,
'sender' => WCF::getUser()->userID,
'username' => WCF::getUser()->username,
'receiver' => $this->parameters['receiver'],
'time' => TIME_NOW,
'type' => $this->parameters['type'],
'message' => $this->parameters['text'],
'enableSmilies' => $this->parameters['enableSmilies'] ? 1 : 0,
'enableHTML' => $this->parameters['enableHTML'] ? 1 : 0,
'color1' => $this->parameters['userData']['color1'],
'color2' => $this->parameters['userData']['color2'],
'additionalData' => serialize($this->parameters['additionalData'])
)
));
$this->objectAction->executeAction();
$returnValues = $this->objectAction->getReturnValues();
// add activity points
\wcf\system\user\activity\point\UserActivityPointHandler::getInstance()->fireEvent('be.bastelstu.chat.activityPointEvent.message', $returnValues['returnValues']->messageID, WCF::getUser()->userID);
return $returnValues['returnValues'];
}
/**
* Validates getting messages.
*/
public function validateGetMessages() {
// check permissions
WCF::getSession()->checkPermissions(array('admin.chat.canReadLog'));
// read parameters
$this->readInteger('roomID');
$this->readInteger('start');
$this->readInteger('end');
// read and validate room
$this->parameters['room'] = room\RoomCache::getInstance()->getRoom($this->parameters['roomID']);
if ($this->parameters['room'] === null) throw new \wcf\system\exception\IllegalLinkException();
}
/**
* Fetches messages in between the specified timestamps.
*
* @return array Array containing message table, containerID and information about an empty message table.
*/
public function getMessages() {
// read messages
$messages = ViewableMessageList::getMessagesBetween($this->parameters['room'], $this->parameters['start'], $this->parameters['end']);
return array(
'noMessages' => (count($messages) == 0) ? true : null,
'containerID' => $this->parameters['containerID'],
'template' => WCF::getTPL()->fetch('__messageLogTable', 'chat', array('messages' => $messages), true)
);
}
/**
* Validates setting an attachment.
*/
public function validateSendAttachment() {
// read user data
$this->parameters['userData']['color1'] = WCF::getUser()->chatColor1;
$this->parameters['userData']['color2'] = WCF::getUser()->chatColor2;
$this->parameters['userData']['roomID'] = WCF::getUser()->chatRoomID;
$this->parameters['userData']['away'] = WCF::getUser()->chatAway;
// read and validate room
$this->parameters['room'] = room\RoomCache::getInstance()->getRoom($this->parameters['userData']['roomID']);
if ($this->parameters['room'] === null) throw new \wcf\system\exception\IllegalLinkException();
if (!$this->parameters['room']->canEnter()) throw new \wcf\system\exception\PermissionDeniedException();
if (!$this->parameters['room']->canWrite()) throw new \wcf\system\exception\PermissionDeniedException();
$this->readInteger('objectID');
if (!$this->parameters['objectID']) throw new \wcf\system\exception\IllegalLinkException();
$this->readInteger('parentObjectID');
if (!$this->parameters['parentObjectID']) throw new \wcf\system\exception\IllegalLinkException();
$editor = new \wcf\data\user\UserEditor(WCF::getUser());
$editor->update(array(
'chatAway' => null,
'chatLastActivity' => TIME_NOW
));
// mark user as back
if ($this->parameters['userData']['away'] !== null) {
$messageAction = new MessageAction(array(), 'create', array(
'data' => array(
'roomID' => $this->parameters['room']->roomID,
'sender' => WCF::getUser()->userID,
'username' => WCF::getUser()->username,
'time' => TIME_NOW,
'type' => Message::TYPE_BACK,
'message' => '',
'color1' => $this->parameters['userData']['color1'],
'color2' => $this->parameters['userData']['color2']
)
));
$messageAction->executeAction();
}
}
/**
* Creates a message linked to an attachment
*/
public function sendAttachment() {
$this->parameters['type'] = Message::TYPE_ATTACHMENT;
$this->parameters['text'] = '[attach]'. $this->parameters['objectID'] .'[/attach]';
$this->parameters['additionalData'] = null;
$this->parameters['receiver'] = null;
$this->parameters['enableSmilies'] = false;
$this->parameters['enableHTML'] = false;
$messageAction = new MessageAction(array(), 'send', $this->parameters);
$messageAction->executeAction();
$returnValues = $messageAction->getReturnValues();
$attachmentHandler = new \wcf\system\attachment\AttachmentHandler('be.bastelstu.chat.message', $this->parameters['objectID'], $this->parameters['tmpHash'], 0);
$attachmentHandler->updateObjectID($returnValues['returnValues']->messageID);
}
}

View File

@ -0,0 +1,41 @@
<?php
namespace chat\data\message;
/**
* Provides functions to edit chat messages.
*
* @author Tim Düsterhus
* @copyright 2010-2014 Tim Düsterhus
* @license Creative Commons Attribution-NonCommercial-ShareAlike <http://creativecommons.org/licenses/by-nc-sa/3.0/legalcode>
* @package be.bastelstu.chat
* @subpackage chat.message
*/
class MessageEditor extends \wcf\data\DatabaseObjectEditor {
/**
* @see \wcf\data\DatabaseObjectDecorator::$baseClass
*/
protected static $baseClass = '\chat\data\message\Message';
/**
* @see wcf\data\DatabaseObjectEditor::deleteAll()
*/
public static function deleteAll(array $objectIDs = array()) {
$count = parent::deleteAll($objectIDs);
if ($count) {
// delete attached files
\wcf\system\attachment\AttachmentHandler::removeAttachments('be.bastelstu.chat.message', $objectIDs);
}
return $count;
}
/**
* Notify the Push-Server.
*/
public static function create(array $parameters = array()) {
\wcf\system\nodePush\NodePushHandler::getInstance()->sendMessage('be.bastelstu.chat.newMessage');
return parent::create($parameters);
}
}

View File

@ -0,0 +1,82 @@
<?php
namespace chat\data\message;
/**
* Represents a list of chat messages.
*
* @author Tim Düsterhus
* @copyright 2010-2014 Tim Düsterhus
* @license Creative Commons Attribution-NonCommercial-ShareAlike <http://creativecommons.org/licenses/by-nc-sa/3.0/legalcode>
* @package be.bastelstu.chat
* @subpackage chat.room
*/
class MessageList extends \wcf\data\DatabaseObjectList {
/**
* @see \wcf\data\DatabaseObjectList::$className
*/
public $className = 'chat\data\message\Message';
/**
* Reads the newest messages for the given room.
*
* @param \chat\data\room\Room $room
* @param integer $number
* @return array<\chat\data\message\Message>
*/
public static function getNewestMessages(\chat\data\room\Room $room, $number = CHAT_LASTMESSAGES) {
if ($number === 0) return array();
$messageList = new static();
$messageList->sqlOrderBy = "message.messageID DESC";
$messageList->sqlLimit = $number;
$messageList->getConditionBuilder()->add('message.receiver IS NULL', array());
$messageList->getConditionBuilder()->add('message.roomID = ?', array($room->roomID));
$messageList->readObjects();
return array_reverse($messageList->getObjects());
}
/**
* Reads the messages since the given message-id for the given room.
*
* @param \chat\data\room\Room $room
* @param integer $since
* @return array<\chat\data\message\Message>
*/
public static function getMessagesSince(\chat\data\room\Room $room, $since) {
$messageList = new static();
$messageList->sqlOrderBy = "message.messageID ASC";
$messageList->getConditionBuilder()->add('message.messageID > ?', array($since));
$messageList->getConditionBuilder()->add('
((
message.receiver IS NULL
AND message.roomID = ?
)
OR message.receiver = ?
OR message.sender = ?)',
array($room->roomID, \wcf\system\WCF::getUser()->userID, \wcf\system\WCF::getUser()->userID)
);
$messageList->readObjects();
return $messageList->getObjects();
}
/**
* Reads the message between the given timestamps for the given room.
*
* @param \chat\data\room\Room $room
* @param integer $start
* @param integer $end
* @return array<\chat\data\message\Message>
*/
public static function getMessagesBetween(\chat\data\room\Room $room, $start, $end) {
$messageList = new static();
$messageList->sqlOrderBy = "message.messageID ASC";
$messageList->getConditionBuilder()->add('message.receiver IS NULL', array());
$messageList->getConditionBuilder()->add('message.roomID = ?', array($room->roomID));
$messageList->getConditionBuilder()->add('message.time BETWEEN ? AND ?', array($start, $end));
$messageList->readObjects();
return $messageList->getObjects();
}
}

View File

@ -0,0 +1,54 @@
<?php
namespace chat\data\message;
/**
* Represents a viewable chat message.
*
* @author Tim Düsterhus
* @copyright 2010-2014 Tim Düsterhus
* @license Creative Commons Attribution-NonCommercial-ShareAlike <http://creativecommons.org/licenses/by-nc-sa/3.0/legalcode>
* @package be.bastelstu.chat
* @subpackage data.message
*/
class ViewableMessage extends \wcf\data\DatabaseObjectDecorator {
/**
* @see \wcf\data\DatabaseObjectDecorator::$baseClass
*/
protected static $baseClass = 'chat\data\message\Message';
/**
* user profile object
* @var \wcf\data\user\UserProfile
*/
protected $userProfile = null;
/**
* Returns the profile object of the user who created the post.
*
* @return wcf\data\user\UserProfile
*/
public function getUserProfile() {
if ($this->userProfile === null) {
$this->userProfile = new \wcf\data\user\UserProfile(new \wcf\data\user\User(null, $this->getDecoratedObject()->data));
}
return $this->userProfile;
}
/**
* @see \chat\data\message\Message::jsonify()
*/
public function jsonify($raw = false) {
$array = parent::jsonify(true);
$array['avatar'] = array(
16 => $this->getUserProfile()->getAvatar()->getImageTag(16),
24 => $this->getUserProfile()->getAvatar()->getImageTag(24),
32 => $this->getUserProfile()->getAvatar()->getImageTag(32),
48 => $this->getUserProfile()->getAvatar()->getImageTag(48)
);
if ($raw) return $array;
return \wcf\util\JSON::encode($array);
}
}

View File

@ -0,0 +1,35 @@
<?php
namespace chat\data\message;
/**
* Represents a list of viewable chat messages.
*
* @author Tim Düsterhus
* @copyright 2010-2014 Tim Düsterhus
* @license Creative Commons Attribution-NonCommercial-ShareAlike <http://creativecommons.org/licenses/by-nc-sa/3.0/legalcode>
* @package be.bastelstu.chat
* @subpackage chat.room
*/
class ViewableMessageList extends MessageList {
/**
* @see \wcf\data\DatabaseObjectList::$decoratorClassName
*/
public $decoratorClassName = 'chat\data\message\ViewableMessage';
/**
* @see \wcf\data\DatabaseObjectList::__construct()
*/
public function __construct() {
parent::__construct();
$this->sqlSelects .= "user_avatar.*, user_option_value.*, user_table.*";
$this->sqlJoins .= " LEFT JOIN wcf".WCF_N."_user user_table ON (user_table.userID = message.sender)";
$this->sqlJoins .= " LEFT JOIN wcf".WCF_N."_user_avatar user_avatar ON (user_avatar.avatarID = user_table.avatarID)";
$this->sqlJoins .= " LEFT JOIN wcf".WCF_N."_user_option_value user_option_value ON (user_option_value.userID = user_table.userID)";
if (MODULE_USER_RANK) {
$this->sqlSelects .= ",user_rank.*";
$this->sqlJoins .= " LEFT JOIN wcf".WCF_N."_user_rank user_rank ON (user_rank.rankID = user_table.rankID)";
}
}
}

View File

@ -0,0 +1,213 @@
<?php
namespace chat\data\room;
use \chat\data\suspension\Suspension;
use \wcf\system\WCF;
/**
* Represents a chat room.
*
* @author Tim Düsterhus
* @copyright 2010-2014 Tim Düsterhus
* @license Creative Commons Attribution-NonCommercial-ShareAlike <http://creativecommons.org/licenses/by-nc-sa/3.0/legalcode>
* @package be.bastelstu.chat
* @subpackage data.room
*/
class Room extends \chat\data\CHATDatabaseObject implements \wcf\system\request\IRouteController {
/**
* @see \wcf\data\DatabaseObject::$databaseTableName
*/
protected static $databaseTableName = 'room';
/**
* @see \wcf\data\DatabaseObject::$databaseTableIndexName
*/
protected static $databaseTableIndexName = 'roomID';
/**
* cached users
*
* @var array<\wcf\data\user\UserProfile>
*/
protected static $users = null;
/**
* @see \wcf\data\chat\room\ChatRoom::getTitle();
*/
public function __toString() {
return $this->getTitle();
}
/**
* Returns whether the user is allowed to enter the room.
*
* @param \wcf\data\user\User $user
* @return boolean
*/
public function canEnter(\wcf\data\user\User $user = null) {
if ($user === null) $user = WCF::getUser();
if (!$user->userID) return false;
$user = new \wcf\data\user\UserProfile($user);
if ($this->isPermanent && $user->getPermission('admin.chat.canManageSuspensions')) return true;
if ($this->isPermanent && $user->getPermission('mod.chat.canGban')) return true;
$ph = new \chat\system\permission\PermissionHandler($user->getDecoratedObject());
if ($ph->getPermission($this, 'mod.canAlwaysEnter')) return true;
if ($ph->getPermission($this, 'mod.canBan')) return true;
if (!$ph->getPermission($this, 'user.canEnter')) return false;
$suspensions = Suspension::getSuspensionsForUser($user->getDecoratedObject());
// room suspension
if (isset($suspensions[$this->roomID][Suspension::TYPE_BAN])) {
if ($suspensions[$this->roomID][Suspension::TYPE_BAN]->isValid()) {
return false;
}
}
// global suspension
if (isset($suspensions[null][Suspension::TYPE_BAN])) {
if ($suspensions[null][Suspension::TYPE_BAN]->isValid()) {
return false;
}
}
return true;
}
/**
* Returns whether the user is allowed to write messages in this room.
*
* @param \wcf\data\user\User $user
* @return boolean
*/
public function canWrite(\wcf\data\user\User $user = null) {
if ($user === null) $user = WCF::getUser();
if (!$user->userID) return false;
$user = new \wcf\data\user\UserProfile($user);
if ($user->getPermission('admin.chat.canManageSuspensions')) return true;
if ($user->getPermission('mod.chat.canGmute')) return true;
$ph = new \chat\system\permission\PermissionHandler($user->getDecoratedObject());
if ($ph->getPermission($this, 'mod.canAlwaysWrite')) return true;
if ($ph->getPermission($this, 'mod.canMute')) return true;
if (!$ph->getPermission($this, 'user.canWrite')) return false;
$suspensions = Suspension::getSuspensionsForUser($user->getDecoratedObject());
// room suspension
if (isset($suspensions[$this->roomID][Suspension::TYPE_MUTE])) {
if ($suspensions[$this->roomID][Suspension::TYPE_MUTE]->isValid()) {
return false;
}
}
// global suspension
if (isset($suspensions[null][Suspension::TYPE_MUTE])) {
if ($suspensions[null][Suspension::TYPE_MUTE]->isValid()) {
return false;
}
}
return true;
}
/**
* Returns whether the user is allowed to mute other users in this room.
*
* @return boolean
*/
public function canMute() {
if (WCF::getSession()->getPermission('admin.chat.canManageSuspensions')) return true;
if (WCF::getSession()->getPermission('mod.chat.canGmute')) return true;
$ph = new \chat\system\permission\PermissionHandler();
return $ph->getPermission($this, 'mod.canMute');
}
/**
* Returns whether the user is allowed to ban other users in this room.
*
* @return boolean
*/
public function canBan() {
if (WCF::getSession()->getPermission('admin.chat.canManageSuspensions')) return true;
if (WCF::getSession()->getPermission('mod.chat.canGban')) return true;
$ph = new \chat\system\permission\PermissionHandler();
return $ph->getPermission($this, 'mod.canBan');
}
/**
* Returns the ID of this chatroom.
*
* @see \wcf\system\request\IRouteController
*/
public function getID() {
return $this->roomID;
}
/**
* Returns the name of this chatroom.
*
* @see \wcf\system\request\IRouteController
*/
public function getTitle() {
return \wcf\system\WCF::getLanguage()->get($this->title);
}
/**
* Returns the topic of this chat room
*
* @return string
*/
public function getTopic() {
return \wcf\system\WCF::getLanguage()->get($this->topic);
}
/**
* Returns the users that are currently active in this room.
*
* @return array<\wcf\data\user\UserProfile>
*/
public function getUsers() {
if (self::$users === null) {
$userList = new \wcf\data\user\UserProfileList();
$userList->getConditionBuilder()->add('user_table.chatRoomID IS NOT NULL', array());
$userList->readObjects();
$users = $userList->getObjects();
foreach ($users as $userID => $user) {
if (!isset(self::$users[$user->chatRoomID])) self::$users[$user->chatRoomID] = array();
self::$users[$user->chatRoomID][$userID] = $user;
}
}
if (!isset(self::$users[$this->roomID])) self::$users[$this->roomID] = array();
return self::$users[$this->roomID];
}
/**
* Returns the users that "timed out".
*
* @return \wcf\data\user\UserList
*/
public static function getDeadUsers() {
if (\wcf\system\nodePush\NodePushHandler::getInstance()->isEnabled()) {
$time = TIME_NOW - 180;
}
else {
$time = TIME_NOW;
}
$userList = new \wcf\data\user\UserList();
$userList->getConditionBuilder()->add('user_table.chatRoomID IS NOT NULL', array());
$userList->getConditionBuilder()->add('user_table.chatLastActivity < ?', array($time - 30));
$userList->readObjects();
return $userList;
}
}

View File

@ -0,0 +1,361 @@
<?php
namespace chat\data\room;
use \chat\data\message;
use \chat\util\ChatUtil;
use \wcf\system\exception;
use \wcf\system\WCF;
/**
* Executes chatroom-related actions.
*
* @author Tim Düsterhus
* @copyright 2010-2014 Tim Düsterhus
* @license Creative Commons Attribution-NonCommercial-ShareAlike <http://creativecommons.org/licenses/by-nc-sa/3.0/legalcode>
* @package be.bastelstu.chat
* @subpackage data.room
*/
class RoomAction extends \wcf\data\AbstractDatabaseObjectAction implements \wcf\data\ISortableAction {
/**
* @see \wcf\data\AbstractDatabaseObjectAction::$className
*/
protected $className = '\chat\data\room\RoomEditor';
/**
* @see \wcf\data\AbstractDatabaseObjectAction::$permissionsDelete
*/
protected $permissionsDelete = array('admin.chat.canDeleteRoom');
/**
* @see \wcf\data\AbstractDatabaseObjectAction::$permissionsUpdate
*/
protected $permissionsUpdate = array('admin.chat.canEditRoom');
/**
* Resets cache if any of the listed actions is invoked
* @var array<string>
*/
protected $resetCache = array('create', 'delete', 'toggle', 'update', 'updatePosition', 'prune');
/**
* Fixes create to append new rooms.
*/
public function create() {
$room = parent::create();
WCF::getDB()->beginTransaction();
$sql = "SELECT MAX(showOrder)
FROM ".call_user_func(array($this->className, 'getDatabaseTableName'))."
FOR UPDATE";
$stmt = WCF::getDB()->prepareStatement($sql);
$stmt->execute();
$editor = new RoomEditor($room);
$editor->update(array('showOrder' => ($stmt->fetchColumn() + 1)));
WCF::getDB()->commitTransaction();
return $room;
}
/**
* Deletes temporary rooms that are unused.
*
* @return integer Number of deleted rooms
*/
public function prune() {
$sql = "SELECT
".call_user_func(array($this->className, 'getDatabaseTableIndexName'))."
FROM
".call_user_func(array($this->className, 'getDatabaseTableName'))."
WHERE
permanent = ?
AND roomID NOT IN (
SELECT chatRoomID
FROM wcf".WCF_N."_user
WHERE chatRoomID IS NOT NULL
)";
$stmt = \wcf\system\WCF::getDB()->prepareStatement($sql);
$stmt->execute(array(0));
$objectIDs = array();
while ($objectID = $stmt->fetchColumn()) $objectIDs[] = $objectID;
return call_user_func(array($this->className, 'deleteAll'), $objectIDs);
}
/**
* @see wcf\data\ISortableAction::validateUpdatePosition()
*/
public function validateUpdatePosition() {
// validate permissions
if (is_array($this->permissionsUpdate) && count($this->permissionsUpdate)) {
WCF::getSession()->checkPermissions($this->permissionsUpdate);
}
else {
throw new exception\PermissionDeniedException();
}
if (!isset($this->parameters['data']['structure'])) {
throw new exception\UserInputException('structure');
}
}
/**
* @see wcf\data\ISortableAction::updatePosition()
*/
public function updatePosition() {
$roomList = new RoomList();
$roomList->readObjects();
$i = 0;
WCF::getDB()->beginTransaction();
foreach ($this->parameters['data']['structure'][0] as $roomID) {
$room = $roomList->search($roomID);
if ($room === null) continue;
$editor = new RoomEditor($room);
$editor->update(array('showOrder' => $i++));
}
WCF::getDB()->commitTransaction();
}
/**
* Validates parameters and permissions.
*/
public function validateGetRoomList() {
if (!MODULE_CHAT) throw new exception\IllegalLinkException();
$this->parameters['room'] = RoomCache::getInstance()->getRoom(WCF::getUser()->chatRoomID);
}
/**
* Returns the available rooms.
*/
public function getRoomList() {
$rooms = RoomCache::getInstance()->getRooms();
$result = array();
foreach ($rooms as $room) {
if (!$room->canEnter()) continue;
$result[] = array(
'title' => (string) $room,
'topic' => $room->getTopic(),
'link' => \wcf\system\request\LinkHandler::getInstance()->getLink('Chat', array(
'application' => 'chat',
'object' => $room
)),
'roomID' => (int) $room->roomID,
'active' => $this->parameters['room'] && $room->roomID == $this->parameters['room']->roomID,
'userCount' => count($room->getUsers()),
'permissions' => array(
'canBan' => (boolean) $room->canBan(),
'canMute' => (boolean) $room->canMute()
)
);
}
return $result;
}
/**
* Validates parameters and permissions.
*/
public function validateJoin() {
if (!MODULE_CHAT) throw new exception\IllegalLinkException();
unset($this->parameters['user']);
$this->readInteger('roomID');
$room = RoomCache::getInstance()->getRoom($this->parameters['roomID']);
if ($room === null) throw new exception\UserInputException('roomID');
if (!$room->canEnter()) throw new exception\PermissionDeniedException();
}
/**
* Joins the room.
*/
public function join() {
// user cannot be set during an AJAX request but may be set by the chat itself
if (!isset($this->parameters['user'])) {
$this->parameters['user'] = WCF::getUser();
}
$room = RoomCache::getInstance()->getRoom($this->parameters['roomID']);
if ($room === null) throw new exception\UserInputException();
if (CHAT_DISPLAY_JOIN_LEAVE) {
if ($this->parameters['user']->chatRoomID) {
// leave message
$messageAction = new message\MessageAction(array(), 'create', array(
'data' => array(
'roomID' => $this->parameters['user']->chatRoomID,
'sender' => $this->parameters['user']->userID,
'username' => $this->parameters['user']->username,
'time' => TIME_NOW,
'type' => message\Message::TYPE_LEAVE,
'message' => '',
'color1' => $this->parameters['user']->chatColor1,
'color2' => $this->parameters['user']->chatColor2,
'additionalData' => serialize(array('room' => $room))
)
));
$messageAction->executeAction();
}
$ipAddress = '';
if (LOG_IP_ADDRESS && $this->parameters['user']->userID == WCF::getUser()->userID) $ipAddress = \wcf\util\UserUtil::convertIPv6To4(\wcf\util\UserUtil::getIpAddress());
// join message
$messageAction = new message\MessageAction(array(), 'create', array(
'data' => array(
'roomID' => $room->roomID,
'sender' => $this->parameters['user']->userID,
'username' => $this->parameters['user']->username,
'time' => TIME_NOW,
'type' => message\Message::TYPE_JOIN,
'message' => '',
'color1' => $this->parameters['user']->chatColor1,
'color2' => $this->parameters['user']->chatColor2,
'additionalData' => serialize(array('ipAddress' => $ipAddress))
)
));
$messageAction->executeAction();
}
$newestMessages = message\ViewableMessageList::getNewestMessages($room, CHAT_LASTMESSAGES + CHAT_DISPLAY_JOIN_LEAVE);
// update last seen message
$sql = "SELECT
MAX(messageID)
FROM
chat".WCF_N."_message";
$stmt = WCF::getDB()->prepareStatement($sql);
$stmt->execute();
$editor = new \wcf\data\user\UserEditor($this->parameters['user']);
$editor->update(array(
'chatRoomID' => $room->roomID,
'chatAway' => null,
'chatLastActivity' => TIME_NOW,
'chatLastSeen' => $stmt->fetchColumn() ?: 0
));
// add activity points
$microtime = microtime(true) * 1000;
$result = $microtime & 0xFFFFFFFF;
if ($result > 0x7FFFFFFF) $result -= 0x80000000;
\wcf\system\user\activity\point\UserActivityPointHandler::getInstance()->fireEvent('be.bastelstu.chat.activityPointEvent.join', $result, WCF::getUser()->userID);
// send push message about join
\wcf\system\nodePush\NodePushHandler::getInstance()->sendMessage('be.bastelstu.chat.join');
$messages = array();
foreach ($newestMessages as $message) $messages[] = $message->jsonify(true);
return array(
'title' => (string) $room,
'topic' => $room->getTopic(),
'link' => \wcf\system\request\LinkHandler::getInstance()->getLink('Chat', array(
'application' => 'chat',
'object' => $room
)),
'roomID' => (int) $room->roomID,
'userCount' => count($room->getUsers()),
'permissions' => array(
'canBan' => (boolean) $room->canBan(),
'canMute' => (boolean) $room->canMute()
),
'messages' => $messages,
);
}
/**
* Validates parameters and permissions.
*/
public function validateLeave() {
if (!MODULE_CHAT) throw new exception\IllegalLinkException();
unset($this->parameters['user']);
if (RoomCache::getInstance()->getRoom(WCF::getUser()->chatRoomID) === null) throw new exception\IllegalLinkException();
}
/**
* Leaves the room.
*/
public function leave() {
// user cannot be set during an AJAX request but may be set by the chat itself
if (!isset($this->parameters['user'])) {
$this->parameters['user'] = WCF::getUser();
}
$room = RoomCache::getInstance()->getRoom($this->parameters['user']->chatRoomID);
if ($room === null) throw new exception\UserInputException();
if (CHAT_DISPLAY_JOIN_LEAVE) {
// leave message
$messageAction = new message\MessageAction(array(), 'create', array(
'data' => array(
'roomID' => $room->roomID,
'sender' => $this->parameters['user']->userID,
'username' => $this->parameters['user']->username,
'time' => TIME_NOW,
'type' => message\Message::TYPE_LEAVE,
'message' => '',
'color1' => $this->parameters['user']->chatColor1,
'color2' => $this->parameters['user']->chatColor2
)
));
$messageAction->executeAction();
}
// set current room to null
$editor = new \wcf\data\user\UserEditor($this->parameters['user']);
$editor->update(array(
'chatRoomID' => null
));
\wcf\system\nodePush\NodePushHandler::getInstance()->sendMessage('be.bastelstu.chat.leave');
}
/**
* Forces dead users to leave the chat.
*/
public function removeDeadUsers() {
$deadUsers = Room::getDeadUsers();
foreach ($deadUsers as $deadUser) {
$roomAction = new self(array(), 'leave', array(
'user' => $deadUser
));
$roomAction->executeAction();
}
}
/**
* Validates permissions.
*/
public function validateGetBoxRoomList() {
if (!MODULE_CHAT) throw new \wcf\system\exception\IllegalLinkException();
}
/**
* Returns dashboard roomlist.
*/
public function getBoxRoomList() {
$rooms = RoomCache::getInstance()->getRooms();
foreach ($rooms as $key => $room) {
if (!$room->canEnter()) unset($rooms[$key]);
}
$this->readBoolean('showEmptyRooms', true);
\wcf\system\WCF::getTPL()->assign(array(
'rooms' => $rooms,
'showEmptyRooms' => $this->parameters['showEmptyRooms']
));
return array(
'template' => \wcf\system\WCF::getTPL()->fetch('boxRoomList', 'chat')
);
}
}

View File

@ -0,0 +1,49 @@
<?php
namespace chat\data\room;
/**
* Manages the room cache.
*
* @author Tim Düsterhus
* @copyright 2010-2014 Tim Düsterhus
* @license Creative Commons Attribution-NonCommercial-ShareAlike <http://creativecommons.org/licenses/by-nc-sa/3.0/legalcode>
* @package be.bastelstu.chat
* @subpackage data.room
*/
class RoomCache extends \wcf\system\SingletonFactory {
/**
* list of cached rooms
* @var array<\chat\data\room\Room>
*/
protected $rooms = array();
/**
* @see wcf\system\SingletonFactory::init()
*/
protected function init() {
$this->rooms = \chat\system\cache\builder\RoomCacheBuilder::getInstance()->getData();
}
/**
* Returns a specific room.
*
* @param integer $roomID
* @return \chat\data\room\Room
*/
public function getRoom($roomID) {
if (isset($this->rooms[$roomID])) {
return $this->rooms[$roomID];
}
return null;
}
/**
* Returns all rooms.
*
* @return array<\chat\data\room\Room>
*/
public function getRooms() {
return $this->rooms;
}
}

View File

@ -0,0 +1,68 @@
<?php
namespace chat\data\room;
use \wcf\system\WCF;
/**
* Provides functions to edit chat rooms.
*
* @author Tim Düsterhus
* @copyright 2010-2014 Tim Düsterhus
* @license Creative Commons Attribution-NonCommercial-ShareAlike <http://creativecommons.org/licenses/by-nc-sa/3.0/legalcode>
* @package be.bastelstu.chat
* @subpackage data.room
*/
class RoomEditor extends \wcf\data\DatabaseObjectEditor implements \wcf\data\IEditableCachedObject {
/**
* @see \wcf\data\DatabaseObjectDecorator::$baseClass
*/
protected static $baseClass = '\chat\data\room\Room';
/**
* @see \wcf\data\DatabaseObjectEditor::deleteAll()
*/
public static function deleteAll(array $objectIDs = array()) {
WCF::getDB()->beginTransaction();
foreach ($objectIDs as $objectID) {
\wcf\system\language\I18nHandler::getInstance()->remove('chat.room.title'.$objectID);
\wcf\system\language\I18nHandler::getInstance()->remove('chat.room.topic'.$objectID);
}
$sql = "SELECT
showOrder
FROM
chat".WCF_N."_room
WHERE
roomID = ?
FOR UPDATE";
$select = WCF::getDB()->prepareStatement($sql);
$sql = "UPDATE
chat".WCF_N."_room
SET
showOrder = showOrder - 1
WHERE
showOrder > ?";
$update = WCF::getDB()->prepareStatement($sql);
foreach ($objectIDs as $objectID) {
$select->execute(array($objectID));
$showOrder = $select->fetchColumn();
$select->closeCursor();
$update->execute(array($showOrder));
}
$return = parent::deleteAll($objectIDs);
WCF::getDB()->commitTransaction();
return $return;
}
/**
* Clears the room cache.
*/
public static function resetCache() {
\chat\system\cache\builder\RoomCacheBuilder::getInstance()->reset();
\chat\system\cache\builder\PermissionCacheBuilder::getInstance()->reset();
\wcf\system\nodePush\NodePushHandler::getInstance()->sendDeferredMessage('be.bastelstu.chat.roomChange');
}
}

View File

@ -0,0 +1,18 @@
<?php
namespace chat\data\room;
/**
* Represents a list of chat rooms.
*
* @author Tim Düsterhus
* @copyright 2010-2014 Tim Düsterhus
* @license Creative Commons Attribution-NonCommercial-ShareAlike <http://creativecommons.org/licenses/by-nc-sa/3.0/legalcode>
* @package be.bastelstu.chat
* @subpackage data.room
*/
class RoomList extends \wcf\data\DatabaseObjectList {
/**
* @see \wcf\data\DatabaseObjectList::$className
*/
public $className = 'chat\data\room\Room';
}

View File

@ -0,0 +1,139 @@
<?php
namespace chat\data\suspension;
use \wcf\system\WCF;
/**
* Represents a chat suspension.
*
* @author Tim Düsterhus
* @copyright 2010-2014 Tim Düsterhus
* @license Creative Commons Attribution-NonCommercial-ShareAlike <http://creativecommons.org/licenses/by-nc-sa/3.0/legalcode>
* @package be.bastelstu.chat
* @subpackage data.suspension
*/
class Suspension extends \chat\data\CHATDatabaseObject {
/**
* @see \wcf\data\DatabaseObject::$databaseTableName
*/
protected static $databaseTableName = 'suspension';
/**
* @see \wcf\data\DatabaseObject::$databaseTableIndexName
*/
protected static $databaseTableIndexName = 'suspensionID';
const TYPE_MUTE = 'mute';
const TYPE_BAN = 'ban';
/**
* Returns whether the suspension still is valid.
*
* @return boolean
*/
public function isValid() {
return $this->expires > TIME_NOW;
}
/**
* Returns whether the given user may view this suspension.
*
* @param \wcf\data\user\User $user
* @return boolean
*/
public function isVisible($user = null) {
if ($user === null) $user = WCF::getUser();
$user = new \wcf\data\user\UserProfile($user);
$ph = new \chat\system\permission\PermissionHandler($user->getDecoratedObject());
if ($user->getPermission('admin.chat.canManageSuspensions')) return true;
if ($user->getPermission('mod.chat.canG'.$this->type)) return true;
if (!$this->roomID) return false;
if ($ph->getPermission($this->getRoom(), 'mod.can'.ucfirst($this->type))) return true;
return false;
}
/**
* Returns the room of this suspension.
*
* @return \chat\data\room\Room
*/
public function getRoom() {
if (!$this->roomID) return new \chat\data\room\Room(null, array('roomID' => null));
return \chat\data\room\RoomCache::getInstance()->getRoom($this->roomID);
}
/**
* Returns all the suspensions for the specified user (current user if no user was specified).
*
* @param \wcf\data\user\User $user
* @return array
*/
public static function getSuspensionsForUser(\wcf\data\user\User $user = null) {
if ($user === null) $user = WCF::getUser();
$ush = \wcf\system\user\storage\UserStorageHandler::getInstance();
// load storage
$ush->loadStorage(array($user->userID));
$data = $ush->getStorage(array($user->userID), 'chatSuspensions');
try {
$suspensions = unserialize($data[$user->userID]);
if ($suspensions === false) throw new \wcf\system\exception\SystemException();
}
catch (\wcf\system\exception\SystemException $e) {
$condition = new \wcf\system\database\util\PreparedStatementConditionBuilder();
$condition->add('userID = ?', array($user->userID));
$condition->add('expires > ?', array(TIME_NOW));
$sql = "SELECT
*
FROM
chat".WCF_N."_suspension
".$condition;
$stmt = WCF::getDB()->prepareStatement($sql);
$stmt->execute($condition->getParameters());
$suspensions = array();
while ($suspension = $stmt->fetchObject('\chat\data\suspension\Suspension')) {
$suspensions[$suspension->roomID][$suspension->type] = $suspension;
}
$ush->update($user->userID, 'chatSuspensions', serialize($suspensions));
}
return $suspensions;
}
/**
* Returns the appropriate suspension for user, room and type.
* Returns false if no active suspension was found.
*
* @param \wcf\data\user\User $user
* @param \chat\data\room\Room $room
* @param integer $type
* @return \chat\data\suspension\Suspension
*/
public static function getSuspensionByUserRoomAndType(\wcf\data\user\User $user, \chat\data\room\Room $room, $type) {
$condition = new \wcf\system\database\util\PreparedStatementConditionBuilder();
$condition->add('userID = ?', array($user->userID));
$condition->add('type = ?', array($type));
$condition->add('expires > ?', array(TIME_NOW));
if ($room->roomID) $condition->add('roomID = ?', array($room->roomID));
else $condition->add('roomID IS NULL');
$sql = "SELECT
*
FROM
chat".WCF_N."_suspension
".$condition;
$statement = WCF::getDB()->prepareStatement($sql);
$statement->execute($condition->getParameters());
$row = $statement->fetchArray();
if (!$row) return false;
return new self(null, $row);
}
}

View File

@ -0,0 +1,51 @@
<?php
namespace chat\data\suspension;
use \wcf\system\WCF;
/**
* Executes chat-suspension-related actions.
*
* @author Tim Düsterhus
* @copyright 2010-2014 Tim Düsterhus
* @license Creative Commons Attribution-NonCommercial-ShareAlike <http://creativecommons.org/licenses/by-nc-sa/3.0/legalcode>
* @package be.bastelstu.chat
* @subpackage data.suspension
*/
class SuspensionAction extends \wcf\data\AbstractDatabaseObjectAction {
/**
* @see \wcf\data\AbstractDatabaseObjectAction::$className
*/
protected $className = '\chat\data\suspension\SuspensionEditor';
/**
* @see \wcf\data\AbstractDatabaseObjectAction::$requireACP
*/
protected $requireACP = array('revoke');
/**
* Validates permissions and parameters
*/
public function validateRevoke() {
WCF::getSession()->checkPermissions((array) 'admin.chat.canManageSuspensions');
$this->parameters['revoker'] = WCF::getUser()->userID;
}
/**
* Revokes suspensions.
*/
public function revoke() {
// TODO: ignore revokes if suspension already is revoked
if (!isset($this->parameters['revoker'])) {
$this->parameters['revoker'] = null;
}
$objectAction = new self($this->objectIDs, 'update', array(
'data' => array(
'expires' => TIME_NOW,
'revoker' => $this->parameters['revoker']
)
));
$objectAction->executeAction();
}
}

View File

@ -0,0 +1,28 @@
<?php
namespace chat\data\suspension;
use \wcf\system\WCF;
/**
* Provides functions to edit chat suspensions.
*
* @author Tim Düsterhus
* @copyright 2010-2014 Tim Düsterhus
* @license Creative Commons Attribution-NonCommercial-ShareAlike <http://creativecommons.org/licenses/by-nc-sa/3.0/legalcode>
* @package be.bastelstu.chat
* @subpackage data.chat.suspension
*/
class SuspensionEditor extends \wcf\data\DatabaseObjectEditor implements \wcf\data\IEditableCachedObject {
/**
* @see \wcf\data\DatabaseObjectDecorator::$baseClass
*/
protected static $baseClass = '\chat\data\suspension\Suspension';
/**
* Clears the suspension cache.
*/
public static function resetCache() {
$ush = \wcf\system\user\storage\UserStorageHandler::getInstance();
$ush->resetAll('chatSuspensions');
}
}

View File

@ -0,0 +1,18 @@
<?php
namespace chat\data\suspension;
/**
* Represents a list of chat suspensions.
*
* @author Maximilian Mader
* @copyright 2010-2014 Tim Düsterhus
* @license Creative Commons Attribution-NonCommercial-ShareAlike <http://creativecommons.org/licenses/by-nc-sa/3.0/legalcode>
* @package be.bastelstu.chat
* @subpackage data.suspension
*/
class SuspensionList extends \wcf\data\DatabaseObjectList {
/**
* @see wcf\data\DatabaseObjectList::$className
*/
public $className = 'chat\data\suspension\Suspension';
}

View File

@ -0,0 +1,230 @@
<?php
namespace chat\page;
use \chat\data;
use \wcf\system\exception;
use \wcf\system\WCF;
/**
* Shows the chat-interface
*
* @author Tim Düsterhus
* @copyright 2010-2014 Tim Düsterhus
* @license Creative Commons Attribution-NonCommercial-ShareAlike <http://creativecommons.org/licenses/by-nc-sa/3.0/legalcode>
* @package be.bastelstu.chat
* @subpackage page
*/
class ChatPage extends \wcf\page\AbstractPage {
/**
* @see wcf\page\AbstractPage::$loginRequired
*/
public $loginRequired = true;
/**
* @see \wcf\page\AbstractPage::$neededModules
*/
public $neededModules = array('MODULE_CHAT');
/**
* @see \wcf\page\AbstractPage::$neededPermissions
*/
public $neededPermissions = array();
/**
* The current room.
*
* @var \chat\data\room\Room
*/
public $room = null;
/**
* The given roomID.
*
* @var integer
*/
public $roomID = 0;
/**
* List of accessible rooms.
*
* @var \chat\data\room\RoomList
*/
public $rooms = array();
/**
* List of installed commands.
*
* @var array<string>
*/
public $commands = array();
/**
* List of smilies in the default category.
*
* @var array<\wcf\data\smiley\Smiley>
* @see \wcf\data\smiley\SmileyCache
*/
public $defaultSmilies = array();
/**
* List of all smiley categories.
*
* @var array<\wcf\data\smiley\SmileyCategory>
* @see \wcf\data\smiley\SmileyCache
*/
public $smileyCategories = array();
/**
* attachment handler
* @see \wcf\system\attachment\AttachmentHandler
*/
public $attachmentHandler = null;
/**
* @see wcf\page\AbstractPage::$enableTracking
*/
public $enableTracking = true;
/**
* @see \wcf\page\IPage::assignVariables()
*/
public function assignVariables() {
parent::assignVariables();
$reflection = new \ReflectionClass('\chat\data\message\Message');
WCF::getTPL()->assign(array(
'room' => $this->room,
'roomID' => $this->roomID,
'rooms' => $this->rooms,
'commands' => $this->commands,
'messageTypes' => $reflection->getConstants(),
'defaultSmilies' => $this->defaultSmilies,
'smileyCategories' => $this->smileyCategories,
'attachmentHandler' => $this->attachmentHandler,
'sidebarCollapsed' => \wcf\system\user\collapsible\content\UserCollapsibleContentHandler::getInstance()->isCollapsed('com.woltlab.wcf.collapsibleSidebar', 'be.bastelstu.chat.ChatPage'),
'sidebarName' => 'be.bastelstu.chat.ChatPage'
));
}
/**
* @see \wcf\page\IPage::readData()
*/
public function readData() {
parent::readData();
$this->readRoom();
$this->readCommands();
// get default smilies
if (MODULE_SMILEY) {
$this->smileyCategories = \wcf\data\smiley\SmileyCache::getInstance()->getCategories();
foreach ($this->smileyCategories as $index => $category) {
$category->loadSmilies();
// remove empty categories
if (!count($category) || $category->isDisabled) {
unset($this->smileyCategories[$index]);
}
}
$firstCategory = reset($this->smileyCategories);
if ($firstCategory) {
$this->defaultSmilies = \wcf\data\smiley\SmileyCache::getInstance()->getCategorySmilies($firstCategory->categoryID ?: null);
}
}
if (MODULE_ATTACHMENT) {
$this->attachmentHandler = new \wcf\system\attachment\AttachmentHandler('be.bastelstu.chat.message', 0, \wcf\util\StringUtil::getRandomID(), 0);
}
}
/**
* @see \wcf\page\IPage::readParameters()
*/
public function readParameters() {
parent::readParameters();
if (isset($_REQUEST['id'])) $this->roomID = (int) $_REQUEST['id'];
}
/**
* Reads installed commands.
*/
public function readCommands() {
$regex = new \wcf\system\Regex('Command.class.php$');
$directory = \wcf\util\DirectoryUtil::getInstance(CHAT_DIR.'lib/system/command/commands/', false);
$files = $directory->getFiles(SORT_ASC, $regex);
foreach ($files as $file) {
$command = $regex->replace(basename($file), '');
if ($command == 'Plain') continue;
$this->commands[] = mb_strtolower($command);
}
$this->commands = array_merge($this->commands, array_keys(\chat\system\command\CommandHandler::getAliasMap()));
sort($this->commands);
}
/**
* Reads room data.
*/
public function readRoom() {
$this->rooms = data\room\RoomCache::getInstance()->getRooms();
if ($this->roomID === 0) {
// no room given
if (CHAT_FORCE_ROOM_SELECT) {
$this->rooms = array_filter($this->rooms, function ($room) {
return $room->canEnter();
});
return;
}
else {
$room = reset($this->rooms);
if ($room === null) {
// no valid room found
throw new exception\IllegalLinkException();
}
// redirect to first chat-room
\wcf\util\HeaderUtil::redirect(\wcf\system\request\LinkHandler::getInstance()->getLink('Chat', array(
'application' => 'chat',
'object' => $room
)));
exit;
}
}
if (!isset($this->rooms[$this->roomID])) throw new exception\IllegalLinkException();
$this->room = $this->rooms[$this->roomID];
if (!$this->room->canEnter()) throw new exception\PermissionDeniedException();
}
/**
* @see \wcf\page\IPage::show()
*/
public function show() {
\wcf\system\menu\page\PageMenu::getInstance()->setActiveMenuItem('chat.header.menu.chat');
// remove index breadcrumb
WCF::getBreadcrumbs()->remove(0);
parent::show();
}
/**
* @see wcf\page\ITrackablePage::getObjectType()
*/
public function getObjectType() {
return 'be.bastelstu.chat.room';
}
/**
* @see wcf\page\ITrackablePage::getObjectID()
*/
public function getObjectID() {
if ($this->room === null) return 0;
return $this->room->roomID;
}
}

View File

@ -0,0 +1,91 @@
<?php
namespace chat\page;
/**
* Shows information about Tims chat.
*
* @author Tim Düsterhus
* @copyright 2010-2014 Tim Düsterhus
* @license Creative Commons Attribution-NonCommercial-ShareAlike <http://creativecommons.org/licenses/by-nc-sa/3.0/legalcode>
* @package be.bastelstu.chat
* @subpackage page
*/
class CopyrightPage extends \wcf\page\AbstractPage {
/**
* @see wcf\page\AbstractPage::$loginRequired
*/
public $loginRequired = true;
/**
* @see \wcf\page\AbstractPage::$neededModules
*/
public $neededModules = array('MODULE_CHAT');
/**
* @see \wcf\page\AbstractPage::$neededPermissions
*/
public $neededPermissions = array();
/**
* @see \wcf\page\AbstractPage::$templateName
*/
public $templateName = '__copyright';
/**
* @see \wcf\page\IPage::assignVariables()
*/
public function assignVariables() {
parent::assignVariables();
$images = explode("\n\n", file_get_contents(__FILE__, null, null, __COMPILER_HALT_OFFSET__ + 2));
\wcf\system\WCF::getTPL()->assign(array(
'background' => str_replace("\n", '', $images[array_rand($images)])
));
}
}
// @codingStandardsIgnoreStart
__halt_compiler();/*iVBORw0KGgoAAAANSUhEUgAAAJAAAACQCAYAAADnRuK4AAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A
/wD/oL2nkwAAAAlwSFlzAAAOwgAADsIBFShKgAAAAAd0SU1FB9wBEhQiMYMbjeYAAARlSURBVHja
7d2/i5RHHMfxfS6b+AO54qocYrBQPAMxXSBF2hRiYWEl6QzBSmyMleBhFUNyxBQGNRDwR6O1iK1d
bNRAPNAiEJazSXMY7gy661+Qm4EdZ+eZeX3q4WaeZ97P+zs7O7dPN+hnFgd1Zq1vA54biABIACQA
EgCJAEgAJACSFtJl7i/JBuBkMkkymJ3LV4uajI3z36T6U9k2JId9pH48Hnv0C0kvAUplIGEgARAD
AQhAAFLChIEEQAzULkDBTcL19fWiDNRt31GlWbuui9mwXSsNIKWnQbMO3SAAMRCAGEgAxEAAAhCA
lDAAMVCb6eP9iT2RGNyYGo1GRV3Y7guXw432HSpqzB88ehBs8+LScpK+FhYWYpoFNxuH1T49hcER
k/f2fdK7MpcMIOubNtdJ9RoIQAASJUwAxEAAYiAAMdA7yPMnZY3nw496d597CdCBlevhRhH7QJPL
F4qajO6H2+FrP/djsM3T5dNK2LRw1GrN0jYbmy5hta7beglQHyej1k+ODAQgAAEIQEoYgBioRYBy
Xle9BorYJJxMjvRuzDGbjTnnohtEnDbccf5KeDL+fhZss/n+tjSjjtgHennycJV22fXL3WCb7U8f
hiF7tRls89+Nn/MZaG5/xObecFu2G93yl7sxc9FtbpRVwqxv2gyABEAAAhCAAAQgAAFIZgXQ+FnE
JljMPlCisz6O2PYMoI1rF5P8nZhTeQxUo4EKe+IB1DOASpswADEQgBgIQAACkLRYwnyMZyAGqgWg
N8//yPfEx5zKi/mvUwBNPacxiTqRmOolKakyf+VesM1fxz6rcuL33vk92Gbn61fBNv+e/SoMRxf+
Cc0oAzkwX19SVYxhzs4AVN/9YSAGYiAAMVCxTxiAGEhmXcKsgRgIQC3ZJeJ06Hh8tN0SFrNRdvCn
m8E2f546XtR1fXzpVvja9x4Itnmxco6BpoYs4kaXdl0xYy5tvqpdRLdc5or7fSA32oMBIA+GEgYg
BgIQAwHIdTGQ68oJ0KHvrgbbPP726yQD+vTitWCbbs/+JH19vvJbURMf8x6MXpawmAlLNehUcOSc
MAZSDiyiZw2Q8zcMxEAAYiDxrgxRwoSBZjTo4RdfImCLvH5wv6iHOevLVmKy+ev3KMkwF4/OnAi2
WVpaymegnBuAMv1cZD3SKhbaAJJ3s2Z1KxkIQMJAwkACoP9pZHOvmKSai/H4nzRbBpHtFvv2ZMT8
vl+tfSXMWu9KWM7vcWrtK6sRa63NLffVNEAMBCBWYCAAAUgJU8IYSF8AYgUGGkRsKA0SvbSFgaa/
h/Pz86nmtCwDtfxOMSWsQo0rYQzEQAzEQABiIAZiIAZiIAYCEIAAVITGR6NRtvHk7IuBBEAWksJA
AiBRwgRADAQgBhIGklmky9xf8NTi6uqqWdkiMb9bOEh02rA4A7GUEgYgKRsgC20AMRCAGEgYSAAk
AFLCqk5X4JgWTcuWWStpMHPmQwAkABIACYBEACQAEgBJE3kLMDdR/zhAlCMAAAAASUVORK5CYII=
iVBORw0KGgoAAAANSUhEUgAAAJYAAACWCAYAAAFLBkF0AAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A
/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9oMEgkcFcTqZcUAAAQDSURBVHja
7d3ddas6GIRh4uUm6MP9V5A+1Ma+OTkr8cKWkIT197x3sWMQw2j4JAu8bb3yFXl/f/o75Gxs37Zt
+/7+/vPi4/F4u9Gjje3PG3nm1UbvCR/4w7sdfR216vF4vPzQ7/f+2+H/rbvFDuenha9a+ptbTWvc
3735+1BjJ+Vly350e6fXaWs8b/Dg9ZB1mCXdaY98Llx+Nm+Jew8pHf1jLcNKsR2SI6g0tvff8ROL
7aPW3RIO50989xXbqbnWTWyHrMNcI7ZDakcX232n9plET7LiFQ3bz3S0g5gItRu25/T4SEPDsO6/
Fx55lFyloxn7fCU4u6Ojz8WqlSzFjpQ7uoyVejK7YUc7Lm1MVsPOntLcU18cF7FOEPn/heLiCj9V
vSRlcOqSdMvcQch8bfyCTKUIzDwAKRp83EZWaj9zXXxRZYSplaoyPEut5btWqstGXTpezD3tQxl9
rzH+i4wlwxpGP5owT51EFwldjglrjRPHVepoPiqlSlgiPKNVwruuH/l/VUKfkTC1UtkDhwzGHjhM
Y/TwdErDwWkOJafd6dOoJRoFANlVVU1KKrTQUizRTqwxumG0i5UMHxMnDQNnTeCsvZZralH6XR9n
NRzRX5EzWXzayZxFrPYBH11UcdTlruwqqfsrXe7OWT0GfEU3NC9NOKtnZ6W6o4cCl7OINWA3TAnq
mrMOKnizDnXGigXbN+ugdHA1VDoI+JqB3whfWKwS8CHBfSHRpaGlkzmLWMQiFrGIBQAAgBOMtA4+
tBZLUUosYhFr5YC/7A6LE99k+8JCNySWzErKp07v3QmcpRsSi1grculahytvn2txQeEsYhGLWDNW
8EnV+qfvgEi5za7munjOIhax5i5Ka+ZMDzManEUsYhFLwBfOFPR6mx1nEYtYxFo24K+sxHuo6jmL
WMRqzsfXOuR+PZaaT9Y66IbEIhYuKEprBjVn6YbEArHaVfBJVX0D3GGhGxKLWPhABV/ziWup2+Is
3ZBYxAKxiEUsYhGLWCAWsQAAAAAAQGu+FjjGFkugwurGMpUFxgJjgbGAtYr3KkV3i5vPKj/wd8iB
gMQCY4GxwFjAPMX7ZT+5NhuJA4HuCnyJBcYCY4GxgPrcR2781T9pdyWzD04kFhgLjIXFafIkxFrP
PB+5TkmtDys+H/6jk6gSC4wFxgJjAfW5z36ADYpkKzMkFhgLjAVMX2PVrHfUThILjAXGAhgLivfM
QrnXFRCzr8yQWGAsMBbAWGjD9M9uaHGLWM3C3LMbAMYCY4GxgB8sm4HEAmOBsYD6TP/LFBPglykA
xgJjgbGAbdvGnyBNKWz3wbclsQDGAmOBsQDGAmOBsQDGAmOBscBYJABjgbEAAAAAAAPwD1TbcSbk
TnGiAAAAAElFTkSuQmCC

View File

@ -0,0 +1,161 @@
<?php
namespace chat\page;
use \chat\data;
use \wcf\system\exception\IllegalLinkException;
use \wcf\system\WCF;
/**
* Loads new messages.
*
* @author Tim Düsterhus
* @copyright 2010-2014 Tim Düsterhus
* @license Creative Commons Attribution-NonCommercial-ShareAlike <http://creativecommons.org/licenses/by-nc-sa/3.0/legalcode>
* @package be.bastelstu.chat
* @subpackage page
*/
class NewMessagesPage extends \wcf\page\AbstractPage {
/**
* @see wcf\page\AbstractPage::$loginRequired
*/
public $loginRequired = true;
/**
* The new and unseen messages.
*
* @var array<\wcf\data\chat\message\ChatMessage>
*/
public $messages = array();
/**
* @see \wcf\page\AbstractPage::$neededModules
*/
public $neededModules = array('MODULE_CHAT');
/**
* @see \wcf\page\AbstractPage::$neededPermissions
*/
public $neededPermissions = array();
/**
* The room the user joined.
*
* @var \chat\data\room\Room
*/
public $room = null;
/**
* All the users that are currently in the room $this->room.
*
* @var array<\wcf\data\user\UserProfile>
*/
public $users = array();
/**
* @see \wcf\page\AbstractPage::$useTemplate
*/
public $useTemplate = false;
/**
* @see \wcf\page\Page::readData()
*/
public function readData() {
parent::readData();
$this->readRoom();
$this->readMessages();
$this->users = $this->room->getUsers();
$roomAction = new data\room\RoomAction(array(), 'removeDeadUsers');
$roomAction->executeAction();
}
/**
* Fetches the new messages
*/
public function readMessages() {
$this->messages = data\message\ViewableMessageList::getMessagesSince($this->room, WCF::getUser()->chatLastSeen);
// update last seen message
$sql = "SELECT
MAX(messageID)
FROM
chat".WCF_N."_message";
$stmt = WCF::getDB()->prepareStatement($sql);
$stmt->execute();
$editor = new \wcf\data\user\UserEditor(WCF::getUser());
$editor->update(array(
'chatLastSeen' => $stmt->fetchColumn(),
'chatLastActivity' => TIME_NOW
));
}
/**
* Initializes the room databaseobject.
*/
public function readRoom() {
$this->room = \chat\data\room\RoomCache::getInstance()->getRoom(WCF::getUser()->chatRoomID);
if (!$this->room) throw new IllegalLinkException();
if (!$this->room->canEnter()) throw new \wcf\system\exception\PermissionDeniedException();
}
/**
* @see \wcf\page\IPage::show()
*/
public function show() {
parent::show();
@header('Content-type: application/json');
\wcf\util\HeaderUtil::sendNoCacheHeaders();
$json = array('users' => array(), 'messages' => array());
foreach ($this->messages as $message) {
$json['messages'][] = $message->jsonify(true);
}
\wcf\system\user\storage\UserStorageHandler::getInstance()->loadStorage(array_keys($this->users));
foreach ($this->users as $user) {
$json['users'][] = array(
'userID' => (int) $user->userID,
'username' => $user->username,
'awayStatus' => $user->chatAway,
'suspended' => (boolean) !$this->room->canWrite($user->getDecoratedObject()),
'avatar' => array(
16 => $user->getAvatar()->getImageTag(16),
24 => $user->getAvatar()->getImageTag(24),
32 => $user->getAvatar()->getImageTag(32),
48 => $user->getAvatar()->getImageTag(48)
),
'link' => \wcf\system\request\LinkHandler::getInstance()->getLink('User', array(
'object' => $user->getDecoratedObject()
))
);
}
if (ENABLE_BENCHMARK) {
$b = \wcf\system\benchmark\Benchmark::getInstance();
$items = array();
if (ENABLE_DEBUG_MODE) {
foreach ($b->getItems() as $item) {
$items[] = array(
'text' => $item['text'],
'use' => $item['use'],
'trace' => $item['trace']
);
}
}
$json['benchmark'] = array(
'time' => $b->getExecutionTime(),
'queryTime' => $b->getQueryExecutionTime(),
'queryPercent' => $b->getQueryExecutionTime() / $b->getExecutionTime(),
'items' => $items
);
}
echo \wcf\util\JSON::encode($json);
exit;
}
}

View File

@ -0,0 +1,23 @@
<?php
namespace chat\system;
/**
* Chat core
*
* @author Tim Düsterhus
* @copyright 2010-2014 Tim Düsterhus
* @license Creative Commons Attribution-NonCommercial-ShareAlike <http://creativecommons.org/licenses/by-nc-sa/3.0/legalcode>
* @package be.bastelstu.chat
* @subpackage system
*/
class CHATCore extends \wcf\system\application\AbstractApplication {
/**
* @see wcf\system\application\AbstractApplication::$abbreviation
*/
protected $abbreviation = 'chat';
/**
* @see wcf\system\application\AbstractApplication::$primaryController
*/
protected $primaryController = 'chat\\page\\ChatPage';
}

View File

@ -0,0 +1,69 @@
<?php
namespace chat\system\attachment;
use \wcf\system\WCF;
/**
* Attachment object type implementation for chat messages.
*
* @author Maximilian Mader
* @copyright 2010-2014 Tim Düsterhus
* @license Creative Commons Attribution-NonCommercial-ShareAlike <http://creativecommons.org/licenses/by-nc-sa/3.0/legalcode>
* @package be.bastelstu.chat
* @subpackage system.attachment
*/
class MessageAttachmentObjectType extends \wcf\system\attachment\AbstractAttachmentObjectType {
/**
* @see wcf\system\attachment\IAttachmentObjectType::getMaxSize()
*/
public function getMaxSize() {
return WCF::getSession()->getPermission('user.chat.maxAttachmentSize');
}
/**
* @see wcf\system\attachment\IAttachmentObjectType::getAllowedExtensions()
*/
public function getAllowedExtensions() {
return \wcf\util\ArrayUtil::trim(explode("\n", WCF::getSession()->getPermission('user.chat.allowedAttachmentExtensions')));
}
/**
* @see wcf\system\attachment\IAttachmentObjectType::getMaxCount()
*/
public function getMaxCount() {
return 1;
}
/**
* @see wcf\system\attachment\IAttachmentObjectType::canDownload()
*/
public function canDownload($objectID) {
if ($objectID) {
$message = new \chat\data\message\Message($objectID);
if (isset($message->roomID) && $message->roomID) {
$room = \chat\data\room\RoomCache::getInstance()->getRoom($message->roomID);
if ($room && $room->canEnter()) return true;
}
}
return false;
}
/**
* @see wcf\system\attachment\IAttachmentObjectType::canUpload()
*/
public function canUpload($objectID, $parentObjectID = 0) {
if ($objectID) {
$room = \chat\data\room\RoomCache::getInstance()->getRoom($parentObjectID);
if (!$room || !$room->canWrite()) return false;
}
return WCF::getSession()->getPermission('user.chat.canUploadAttachment');
}
/**
* @see wcf\system\attachment\IAttachmentObjectType::canDelete()
*/
public function canDelete($objectID) {
return false;
}
}

View File

@ -0,0 +1,41 @@
<?php
namespace chat\system\cache\builder;
use wcf\system\WCF;
/**
* Caches the chat permissions for a combination of user groups.
*
* @author Tim Düsterhus, Marcel Werk
* @copyright 2010-2014 WoltLab GmbH
* @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
* @package be.bastelstu.chat
* @subpackage system.cache.builder
*/
class PermissionCacheBuilder extends \wcf\system\cache\builder\AbstractCacheBuilder {
/**
* @see wcf\system\cache\AbstractCacheBuilder::rebuild()
*/
public function rebuild(array $parameters) {
$data = array();
if (!empty($parameters)) {
$conditionBuilder = new \wcf\system\database\util\PreparedStatementConditionBuilder();
$conditionBuilder->add('acl_option.objectTypeID = ?', array(\wcf\system\acl\ACLHandler::getInstance()->getObjectTypeID('be.bastelstu.chat.room')));
$conditionBuilder->add('option_to_group.optionID = acl_option.optionID');
$conditionBuilder->add('option_to_group.groupID IN (?)', array($parameters));
$sql = "SELECT option_to_group.groupID, option_to_group.objectID AS roomID, option_to_group.optionValue,
acl_option.optionName AS permission
FROM wcf".WCF_N."_acl_option acl_option,
wcf".WCF_N."_acl_option_to_group option_to_group
".$conditionBuilder;
$stmt = WCF::getDB()->prepareStatement($sql);
$stmt->execute($conditionBuilder->getParameters());
while ($row = $stmt->fetchArray()) {
if (!isset($data[$row['roomID']][$row['permission']])) $data[$row['roomID']][$row['permission']] = $row['optionValue'];
else $data[$row['roomID']][$row['permission']] = $row['optionValue'] || $data[$row['roomID']][$row['permission']];
}
}
return $data;
}
}

View File

@ -0,0 +1,25 @@
<?php
namespace chat\system\cache\builder;
/**
* Caches all chat rooms.
*
* @author Tim Düsterhus
* @copyright 2010-2014 Tim Düsterhus
* @license Creative Commons Attribution-NonCommercial-ShareAlike <http://creativecommons.org/licenses/by-nc-sa/3.0/legalcode>
* @package be.bastelstu.chat
* @subpackage system.cache.builder
*/
class RoomCacheBuilder extends \wcf\system\cache\builder\AbstractCacheBuilder {
/**
* @see \wcf\system\cache\AbstractCacheBuilder::rebuild()
*/
public function rebuild(array $parameters) {
// get all chat rooms
$roomList = new \chat\data\room\RoomList();
$roomList->sqlOrderBy = "room.showOrder";
$roomList->readObjects();
return $roomList->getObjects();
}
}

View File

@ -0,0 +1,69 @@
<?php
namespace chat\system\command;
use \wcf\system\event\EventHandler;
/**
* Default implementation for commands.
*
* @author Tim Düsterhus
* @copyright 2010-2014 Tim Düsterhus
* @license Creative Commons Attribution-NonCommercial-ShareAlike <http://creativecommons.org/licenses/by-nc-sa/3.0/legalcode>
* @package be.bastelstu.chat
* @subpackage system.chat.command
*/
abstract class AbstractCommand implements ICommand {
/**
* Instance of the CommandHandler
*
* @var \chat\system\command\CommandHandler
*/
public $commandHandler = null;
/**
* Should HTML be enabled?
*
* @var integer
* @see \chat\system\command\ICommand::SETTING_OFF
* @see \chat\system\command\ICommand::SETTING_ON
*/
public $enableHTML = ICommand::SETTING_OFF;
/**
* Should smilies be enabled?
*
* @var integer
* @see \chat\system\command\ICommand::SETTING_OFF
* @see \chat\system\command\ICommand::SETTING_ON
* @see \chat\system\command\ICommand::SETTING_USER
*/
public $enableSmilies = ICommand::SETTING_OFF;
public function __construct(CommandHandler $commandHandler) {
EventHandler::getInstance()->fireAction($this, 'shouldInit');
$this->commandHandler = $commandHandler;
}
/**
* Fires the didInit-event.
* You should call this when everything is properly inserted.
*/
public function didInit() {
EventHandler::getInstance()->fireAction($this, 'didInit');
}
/**
* Default-Receiver: Everyone
*
* @see \chat\system\command\ICommand::getReceiver()
*/
public function getReceiver() {
return null;
}
/**
* No additionaldata by default.
*/
public function getAdditionalData() {
return null;
}
}

View File

@ -0,0 +1,29 @@
<?php
namespace chat\system\command;
use \wcf\system\event\EventHandler;
/**
* Default implementation for restricted commands
*
* @author Tim Düsterhus
* @copyright 2010-2014 Tim Düsterhus
* @license Creative Commons Attribution-NonCommercial-ShareAlike <http://creativecommons.org/licenses/by-nc-sa/3.0/legalcode>
* @package be.bastelstu.chat
* @subpackage system.chat.command
*/
abstract class AbstractRestrictedCommand extends AbstractCommand implements IRestrictedCommand {
public function __construct(CommandHandler $commandHandler) {
parent::__construct($commandHandler);
$this->checkPermission();
}
/**
* Fires checkPermission event.
*
* @see \chat\system\command\IRestrictedCommand
*/
public function checkPermission() {
EventHandler::getInstance()->fireAction($this, 'checkPermission');
}
}

View File

@ -0,0 +1,126 @@
<?php
namespace chat\system\command;
use \chat\data\suspension;
use \chat\util\ChatUtil;
use \wcf\data\user\User;
use \wcf\system\WCF;
/**
* Default implementation for suspension commands
*
* @author Tim Düsterhus
* @copyright 2010-2014 Tim Düsterhus
* @license Creative Commons Attribution-NonCommercial-ShareAlike <http://creativecommons.org/licenses/by-nc-sa/3.0/legalcode>
* @package be.bastelstu.chat
* @subpackage system.chat.command
*/
abstract class AbstractSuspensionCommand extends AbstractRestrictedCommand {
public $user = null;
public $expires = 0;
public $suspensionAction = null;
public $link = '';
public $room = null;
public $reason = '';
public function __construct(\chat\system\command\CommandHandler $commandHandler) {
parent::__construct($commandHandler);
try {
$parameters = explode(',', $commandHandler->getParameters(), 3);
list($username, $modifier) = $parameters;
if (isset($parameters[2])) {
$this->reason = \wcf\util\StringUtil::trim($parameters[2]);
}
$modifier = ChatUtil::timeModifier(\wcf\util\StringUtil::trim($modifier));
$expires = strtotime($modifier, TIME_NOW);
$this->expires = min(max(-0x80000000, $expires), 0x7FFFFFFF);
}
catch (\wcf\system\exception\SystemException $e) {
throw new \InvalidArgumentException();
}
$this->user = User::getUserByUsername($username);
if (!$this->user->userID) throw new \chat\system\command\UserNotFoundException($username);
$profile = \wcf\system\request\LinkHandler::getInstance()->getLink('User', array(
'object' => $this->user
));
$this->link = "[url='".$profile."']".$this->user->username.'[/url]';
$this->executeAction();
$this->didInit();
}
public function executeAction() {
if (static::IS_GLOBAL) $room = new \chat\data\room\Room(null, array('roomID' => null));
else $room = $this->room;
if ($suspension = suspension\Suspension::getSuspensionByUserRoomAndType($this->user, $room, static::SUSPENSION_TYPE)) {
if ($suspension->expires >= $this->expires) {
throw new \wcf\system\exception\UserInputException('text', WCF::getLanguage()->get('chat.suspension.exists'));
}
$action = new suspension\SuspensionAction(array($suspension), 'revoke', array(
'revoker' => WCF::getUser()->userID
));
$action->executeAction();
}
$this->suspensionAction = new suspension\SuspensionAction(array(), 'create', array(
'data' => array(
'userID' => $this->user->userID,
'roomID' => $room->roomID ?: null,
'type' => static::SUSPENSION_TYPE,
'expires' => $this->expires,
'time' => TIME_NOW,
'issuer' => WCF::getUser()->userID,
'reason' => $this->reason
)
));
$this->suspensionAction->executeAction();
}
/**
* @see \chat\system\command\IRestrictedChatCommand::checkPermission()
*/
public function checkPermission() {
parent::checkPermission();
$this->room = $this->commandHandler->getRoom();
if (WCF::getSession()->getPermission('admin.chat.canManageSuspensions')) return;
$ph = new \chat\system\permission\PermissionHandler();
if (static::IS_GLOBAL) {
WCF::getSession()->checkPermissions(array('mod.chat.canG'.static::SUSPENSION_TYPE));
}
else {
if (!WCF::getSession()->getPermission('mod.chat.canG'.static::SUSPENSION_TYPE)) {
if (!$ph->getPermission($this->room, 'mod.can'.ucfirst(static::SUSPENSION_TYPE))) {
throw new \wcf\system\exception\PermissionDeniedException();
}
}
}
}
/**
* @see \chat\system\command\ICommand::getType()
*/
public function getType() {
return \chat\data\message\Message::TYPE_MODERATE;
}
/**
* @see \chat\system\command\ICommand::getMessage()
*/
public function getMessage() {
return serialize(array(
'link' => $this->link,
'expires' => $this->expires,
'type' => (static::IS_GLOBAL ? 'g' : '').static::SUSPENSION_TYPE,
'reason' => $this->reason
));
}
}

View File

@ -0,0 +1,93 @@
<?php
namespace chat\system\command;
use \chat\data\suspension;
use \chat\util\ChatUtil;
use \wcf\data\user\User;
use \wcf\system\WCF;
/**
* Default implementation for suspension commands
*
* @author Tim Düsterhus
* @copyright 2010-2014 Tim Düsterhus
* @license Creative Commons Attribution-NonCommercial-ShareAlike <http://creativecommons.org/licenses/by-nc-sa/3.0/legalcode>
* @package be.bastelstu.chat
* @subpackage system.chat.command
*/
abstract class AbstractUnsuspensionCommand extends AbstractRestrictedCommand {
public $user = null;
public $suspensionAction = null;
public $link = '';
public $room = null;
public function __construct(\chat\system\command\CommandHandler $commandHandler) {
parent::__construct($commandHandler);
$username = rtrim($commandHandler->getParameters(), ',');
$this->user = User::getUserByUsername($username);
if (!$this->user->userID) throw new \chat\system\command\UserNotFoundException($username);
$profile = \wcf\system\request\LinkHandler::getInstance()->getLink('User', array(
'object' => $this->user
));
$this->link = "[url='".$profile."']".$this->user->username.'[/url]';
$this->executeAction();
$this->didInit();
}
public function executeAction() {
if (static::IS_GLOBAL) $room = new \chat\data\room\Room(null, array('roomID' => null));
else $room = $this->room;
if ($suspension = suspension\Suspension::getSuspensionByUserRoomAndType($this->user, $room, static::SUSPENSION_TYPE)) {
$action = new suspension\SuspensionAction(array($suspension), 'revoke', array(
'revoker' => WCF::getUser()->userID
));
$action->executeAction();
}
else {
throw new \wcf\system\exception\UserInputException('text', WCF::getLanguage()->get('chat.suspension.notExists'));
}
}
/**
* @see \chat\system\command\IRestrictedChatCommand::checkPermission()
*/
public function checkPermission() {
parent::checkPermission();
$this->room = $this->commandHandler->getRoom();
if (WCF::getSession()->getPermission('admin.chat.canManageSuspensions')) return;
$ph = new \chat\system\permission\PermissionHandler();
if (static::IS_GLOBAL) {
WCF::getSession()->checkPermissions(array('mod.chat.canG'.static::SUSPENSION_TYPE));
}
else {
if (!WCF::getSession()->getPermission('mod.chat.canG'.static::SUSPENSION_TYPE)) {
if (!$ph->getPermission($this->room, 'mod.can'.ucfirst(static::SUSPENSION_TYPE))) {
throw new \wcf\system\exception\PermissionDeniedException();
}
}
}
}
/**
* @see \chat\system\command\ICommand::getType()
*/
public function getType() {
return \chat\data\message\Message::TYPE_MODERATE;
}
/**
* @see \chat\system\command\ICommand::getMessage()
*/
public function getMessage() {
return serialize(array(
'link' => $this->link,
'type' => 'un'.(static::IS_GLOBAL ? 'g' : '').static::SUSPENSION_TYPE,
));
}
}

View File

@ -0,0 +1,126 @@
<?php
namespace chat\system\command;
use \wcf\util\StringUtil;
/**
* Handles commands
*
* @author Tim Düsterhus
* @copyright 2010-2014 Tim Düsterhus
* @license Creative Commons Attribution-NonCommercial-ShareAlike <http://creativecommons.org/licenses/by-nc-sa/3.0/legalcode>
* @package be.bastelstu.chat
* @subpackage system.chat.command
*/
final class CommandHandler {
/**
* char that indicates a command
* @var string
*/
const COMMAND_CHAR = '/';
/**
* message text
* @var string
*/
private $text = '';
/**
* current room
* @var \chat\data\room\Room
*/
private $room = null;
/**
* Initialises the CommandHandler
*
* @param string $text
* @param \chat\data\room\Room $room
*/
public function __construct($text, \chat\data\room\Room $room = null) {
$this->text = $text;
$this->room = $room;
$aliases = self::getAliasMap();
foreach ($aliases as $search => $replace) {
$this->text = \wcf\system\Regex::compile('^'.preg_quote(self::COMMAND_CHAR.$search).'( |$)')->replace($this->text, self::COMMAND_CHAR.$replace.' ');
}
$this->text = \wcf\system\Regex::compile('^//')->replace($this->text, '/plain ');
}
/**
* Returns the alias map. Key is the alias, value is the target.
*
* @return array<string>
*/
public static function getAliasMap() {
if (StringUtil::trim(CHAT_COMMAND_ALIASES) === '') return array();
try {
$result = array();
foreach (explode("\n", StringUtil::unifyNewlines(StringUtil::toLowerCase(CHAT_COMMAND_ALIASES))) as $line) {
list($key, $val) = explode(':', $line, 2);
$result[StringUtil::trim($key)] = StringUtil::trim($val);
}
return $result;
}
catch (\wcf\system\exception\SystemException $e) {
throw new \wcf\system\exception\SystemException("Invalid alias specified: '".$line."'");
}
}
/**
* Checks whether the given text is a command.
*/
public function isCommand($text = null) {
if ($text === null) $text = $this->text;
return StringUtil::startsWith($text, static::COMMAND_CHAR);
}
/**
* Returns the whole message.
*
* @return string
*/
public function getText() {
return $this->text;
}
/**
* Returns the current room.
*
* @return \chat\data\room\Room
*/
public function getRoom() {
return $this->room;
}
/**
* Returns the parameter-string.
*
* @return string
*/
public function getParameters() {
$parts = explode(' ', mb_substr($this->text, mb_strlen(static::COMMAND_CHAR)), 2);
if (!isset($parts[1])) return '';
return $parts[1];
}
/**
* Loads the command.
*/
public function loadCommand() {
$parts = explode(' ', mb_substr($this->text, mb_strlen(static::COMMAND_CHAR)), 2);
$class = '\chat\system\command\commands\\'.ucfirst(strtolower($parts[0])).'Command';
if (!class_exists($class)) {
throw new NotFoundException($parts[0]);
}
return new $class($this);
}
}

View File

@ -0,0 +1,54 @@
<?php
namespace chat\system\command;
/**
* Interface for chat-commands.
*
* @author Tim Düsterhus
* @copyright 2010-2014 Tim Düsterhus
* @license Creative Commons Attribution-NonCommercial-ShareAlike <http://creativecommons.org/licenses/by-nc-sa/3.0/legalcode>
* @package be.bastelstu.chat
* @subpackage system.chat.command
*/
interface ICommand {
/**
* Setting is forced to be disabled.
*
* @var integer
*/
const SETTING_OFF = 0;
/**
* Setting is forced to be enabled.
*
* @var integer
*/
const SETTING_ON = 1;
/**
* The user may decide whether this setting is on or off.
*
* @var integer
*/
const SETTING_USER = 2;
/**
* Returns the message-type for this command.
*/
public function getType();
/**
* Returns the message-text for this command.
*/
public function getMessage();
/**
* Returns additionalData to be saved within database
*/
public function getAdditionalData();
/**
* Returns the receiver for this command.
*/
public function getReceiver();
}

View File

@ -0,0 +1,20 @@
<?php
namespace chat\system\command;
/**
* Interface for Restricted commands.
*
* @author Tim Düsterhus
* @copyright 2010-2014 Tim Düsterhus
* @license Creative Commons Attribution-NonCommercial-ShareAlike <http://creativecommons.org/licenses/by-nc-sa/3.0/legalcode>
* @package be.bastelstu.chat
* @subpackage system.chat.command
*/
interface IRestrictedCommand {
/**
* Checks the permission to use this command. Has to throw
* \wcf\system\exception\PermissionDeniedException when the
* user is not allowed to use the command.
*/
public function checkPermission();
}

View File

@ -0,0 +1,32 @@
<?php
namespace chat\system\command;
/**
* Thrown when a command is not found.
*
* @author Tim Düsterhus
* @copyright 2010-2014 Tim Düsterhus
* @license Creative Commons Attribution-NonCommercial-ShareAlike <http://creativecommons.org/licenses/by-nc-sa/3.0/legalcode>
* @package be.bastelstu.chat
* @subpackage system.chat.command
*/
class NotFoundException extends \Exception {
/**
* given command
* @var string
*/
private $command = '';
public function __construct($command) {
$this->command = $command;
}
/**
* Returns the given command
*
* @return string
*/
public function getCommand() {
return $this->command;
}
}

View File

@ -0,0 +1,32 @@
<?php
namespace chat\system\command;
/**
* Thrown when a user is not found.
*
* @author Tim Düsterhus
* @copyright 2010-2014 Tim Düsterhus
* @license Creative Commons Attribution-NonCommercial-ShareAlike <http://creativecommons.org/licenses/by-nc-sa/3.0/legalcode>
* @package be.bastelstu.chat
* @subpackage system.chat.command
*/
class UserNotFoundException extends \Exception {
/**
* given username
* @var string
*/
private $username = '';
public function __construct($username) {
$this->username = $username;
}
/**
* Returns the given username
*
* @return string
*/
public function getUsername() {
return $this->username;
}
}

View File

@ -0,0 +1,38 @@
<?php
namespace chat\system\command\commands;
use \wcf\system\WCF;
/**
* Marks the user as away.
*
* @author Tim Düsterhus
* @copyright 2010-2014 Tim Düsterhus
* @license Creative Commons Attribution-NonCommercial-ShareAlike <http://creativecommons.org/licenses/by-nc-sa/3.0/legalcode>
* @package be.bastelstu.chat
* @subpackage system.chat.command.commands
*/
class AwayCommand extends \chat\system\command\AbstractCommand {
public function __construct(\chat\system\command\CommandHandler $commandHandler) {
parent::__construct($commandHandler);
$editor = new \wcf\data\user\UserEditor(WCF::getUser());
$editor->update(array(
'chatAway' => $commandHandler->getParameters()
));
$this->didInit();
}
/**
* @see \chat\system\command\ICommand::getType()
*/
public function getType() {
return \chat\data\message\Message::TYPE_AWAY;
}
/**
* @see \chat\system\command\ICommand::getMessage()
*/
public function getMessage() {
return \wcf\system\bbcode\PreParser::getInstance()->parse($this->commandHandler->getParameters(), explode(',', WCF::getSession()->getPermission('user.chat.allowedBBCodes')));
}
}

View File

@ -0,0 +1,16 @@
<?php
namespace chat\system\command\commands;
/**
* Bans a user.
*
* @author Tim Düsterhus
* @copyright 2010-2014 Tim Düsterhus
* @license Creative Commons Attribution-NonCommercial-ShareAlike <http://creativecommons.org/licenses/by-nc-sa/3.0/legalcode>
* @package be.bastelstu.chat
* @subpackage system.chat.command.commands
*/
class BanCommand extends \chat\system\command\AbstractSuspensionCommand {
const IS_GLOBAL = false;
const SUSPENSION_TYPE = \chat\data\suspension\Suspension::TYPE_BAN;
}

View File

@ -0,0 +1,223 @@
<?php
namespace chat\system\command\commands;
/**
* Changes the color of the username
*
* @author Tim Düsterhus
* @copyright 2010-2014 Tim Düsterhus
* @license Creative Commons Attribution-NonCommercial-ShareAlike <http://creativecommons.org/licenses/by-nc-sa/3.0/legalcode>
* @package be.bastelstu.chat
* @subpackage system.chat.command.commands
*/
class ColorCommand extends \chat\system\command\AbstractCommand {
/**
* Map names to hexcodes
*
* @var array<integer>
*/
public static $colors = array(
'aliceblue' => 0xF0F8FF,
'antiquewhite' => 0xFAEBD7,
'aqua' => 0x00FFFF,
'aquamarine' => 0x7FFFD4,
'azure' => 0xF0FFFF,
'beige' => 0xF5F5DC,
'bisque' => 0xFFE4C4,
'black' => 0x000000,
'blanchedalmond' => 0xFFEBCD,
'blue' => 0x0000FF,
'bluescreenblue' => 0x0000AA,
'blueviolet' => 0x8A2BE2,
'brown' => 0xA52A2A,
'burlywood' => 0xDEB887,
'cadetblue' => 0x5F9EA0,
'chartreuse' => 0x7FFF00,
'chocolate' => 0xD2691E,
'coral' => 0xFF7F50,
'cornflowerblue' => 0x6495ED,
'cornsilk' => 0xFFF8DC,
'crimson' => 0xDC143C,
'cyan' => 0x00FFFF,
'darkblue' => 0x00008B,
'darkcyan' => 0x008B8B,
'darkgoldenrod' => 0xB8860B,
'darkgray' => 0xA9A9A9,
'darkgrey' => 0xA9A9A9,
'darkgreen' => 0x006400,
'darkkhaki' => 0xBDB76B,
'darkmagenta' => 0x8B008B,
'darkolivegreen' => 0x556B2F,
'darkorange' => 0xFF8C00,
'darkorchid' => 0x9932CC,
'darkred' => 0x8B0000,
'darksalmon' => 0xE9967A,
'darkseagreen' => 0x8FBC8F,
'darkslateblue' => 0x483D8B,
'darkslategray' => 0x2F4F4F,
'darkslategrey' => 0x2F4F4F,
'darkturquoise' => 0x00CED1,
'darkviolet' => 0x9400D3,
'deeppink' => 0xFF1493,
'deepskyblue' => 0x00BFFF,
'dimgray' => 0x696969,
'dimgrey' => 0x696969,
'dodgerblue' => 0x1E90FF,
'firebrick' => 0xB22222,
'floralwhite' => 0xFFFAF0,
'forestgreen' => 0x228B22,
'fuchsia' => 0xFF00FF,
'gainsboro' => 0xDCDCDC,
'ghostwhite' => 0xF8F8FF,
'gold' => 0xFFD700,
'goldenrod' => 0xDAA520,
'gray' => 0x808080,
'grey' => 0x808080,
'green' => 0x008000,
'greenyellow' => 0xADFF2F,
'honeydew' => 0xF0FFF0,
'hotpink' => 0xFF69B4,
'indianred' => 0xCD5C5C,
'indigo' => 0x4B0082,
'ivory' => 0xFFFFF0,
'khaki' => 0xF0E68C,
'lavender' => 0xE6E6FA,
'lavenderblush' => 0xFFF0F5,
'lawngreen' => 0x7CFC00,
'lemonchiffon' => 0xFFFACD,
'lightblue' => 0xADD8E6,
'lightcoral' => 0xF08080,
'lightcyan' => 0xE0FFFF,
'lightgoldenrodyellow' => 0xFAFAD2,
'lightgray' => 0xD3D3D3,
'lightgrey' => 0xD3D3D3,
'lightgreen' => 0x90EE90,
'lightpink' => 0xFFB6C1,
'lightsalmon' => 0xFFA07A,
'lightseagreen' => 0x20B2AA,
'lightskyblue' => 0x87CEFA,
'lightslategray' => 0x778899,
'lightslategrey' => 0x778899,
'lightsteelblue' => 0xB0C4DE,
'lightyellow' => 0xFFFFE0,
'lime' => 0x00FF00,
'limegreen' => 0x32CD32,
'linen' => 0xFAF0E6,
'magenta' => 0xFF00FF,
'maroon' => 0x800000,
'mediumaquamarine' => 0x66CDAA,
'mediumblue' => 0x0000CD,
'mediumorchid' => 0xBA55D3,
'mediumpurple' => 0x9370D8,
'mediumseagreen' => 0x3CB371,
'mediumslateblue' => 0x7B68EE,
'mediumspringgreen' => 0x00FA9A,
'mediumturquoise' => 0x48D1CC,
'mediumvioletred' => 0xC71585,
'midnightblue' => 0x191970,
'mintcream' => 0xF5FFFA,
'mistyrose' => 0xFFE4E1,
'moccasin' => 0xFFE4B5,
'navajowhite' => 0xFFDEAD,
'navy' => 0x000080,
'oldlace' => 0xFDF5E6,
'olive' => 0x808000,
'olivedrab' => 0x6B8E23,
'orange' => 0xFFA500,
'orangered' => 0xFF4500,
'orchid' => 0xDA70D6,
'oxford' => 0xF02D, // looks like green
'palegoldenrod' => 0xEEE8AA,
'palegreen' => 0x98FB98,
'paleturquoise' => 0xAFEEEE,
'palevioletred' => 0xD87093,
'papayawhip' => 0xFFEFD5,
'peachpuff' => 0xFFDAB9,
'peru' => 0xCD853F,
'pink' => 0xFFC0CB,
'plum' => 0xDDA0DD,
'powderblue' => 0xB0E0E6,
'purple' => 0x800080,
'red' => 0xFF0000,
'rosybrown' => 0xBC8F8F,
'royalblue' => 0x4169E1,
'saddlebrown' => 0x8B4513,
'sadwin' => 0x2067B2,
'salmon' => 0xFA8072,
'sandybrown' => 0xF4A460,
'seagreen' => 0x2E8B57,
'seashell' => 0xFFF5EE,
'sienna' => 0xA0522D,
'silver' => 0xC0C0C0,
'skyblue' => 0x87CEEB,
'slateblue' => 0x6A5ACD,
'slategray' => 0x708090,
'slategrey' => 0x708090,
'snow' => 0xFFFAFA,
'springgreen' => 0x00FF7F,
'steelblue' => 0x4682B4,
'tan' => 0xD2B48C,
'teal' => 0x008080,
'thistle' => 0xD8BFD8,
'tomato' => 0xFF6347,
'turquoise' => 0x40E0D0,
'violet' => 0xEE82EE,
'wheat' => 0xF5DEB3,
'white' => 0xFFFFFF,
'whitesmoke' => 0xF5F5F5,
'yellow' => 0xFFFF00,
'yellowgreen' => 0x9ACD32
);
public function __construct(\chat\system\command\CommandHandler $commandHandler) {
parent::__construct($commandHandler);
try {
list($color[1], $color[2]) = explode(' ', $commandHandler->getParameters(), 2);
}
catch (\wcf\system\exception\SystemException $e) {
$color[1] = $color[2] = $commandHandler->getParameters();
}
$regex = new \wcf\system\Regex('^#?([a-f0-9]{3}|[a-f0-9]{6})$', \wcf\system\Regex::CASE_INSENSITIVE);
foreach ($color as $key => $val) {
if (isset(self::$colors[$val])) $color[$key] = self::$colors[$val];
else {
if (!$regex->match($val)) throw new \InvalidArgumentException();
$matches = $regex->getMatches();
$val = $matches[1];
if (strlen($val) == 3) $val = $val[0].$val[0].$val[1].$val[1].$val[2].$val[2];
$color[$key] = hexdec($val);
}
}
$editor = new \wcf\data\user\UserEditor(\wcf\system\WCF::getUser());
$editor->update(array(
'chatColor1' => $color[1],
'chatColor2' => $color[2]
));
$this->didInit();
}
/**
* @see \chat\system\command\ICommand::getType()
*/
public function getType() {
return \chat\data\message\Message::TYPE_INFORMATION;
}
/**
* @see \chat\system\command\ICommand::getMessage()
*/
public function getMessage() {
return \wcf\system\WCF::getLanguage()->get('chat.message.color.success');
}
/**
* @see \chat\system\command\ICommand::getReceiver()
*/
public function getReceiver() {
return \wcf\system\WCF::getUser()->userID;
}
}

View File

@ -0,0 +1,30 @@
<?php
namespace chat\system\command\commands;
/**
* Informs everyone that the fish was freed. OH A NOEZ.
*
* @author Tim Düsterhus
* @copyright 2010-2014 Tim Düsterhus
* @license Creative Commons Attribution-NonCommercial-ShareAlike <http://creativecommons.org/licenses/by-nc-sa/3.0/legalcode>
* @package be.bastelstu.chat
* @subpackage system.chat.command.commands
*/
class FreeCommand extends MeCommand {
public function __construct(\chat\system\command\CommandHandler $commandHandler) {
parent::__construct($commandHandler);
if (mb_strtolower($this->commandHandler->getParameters()) != 'the fish') {
throw new \InvalidArgumentException();
}
$this->didInit();
}
/**
* @see \chat\system\command\ICommand::getMessage()
*/
public function getMessage() {
return 'freed the fish. OH A NOEZ';
}
}

View File

@ -0,0 +1,16 @@
<?php
namespace chat\system\command\commands;
/**
* Globally bans a user.
*
* @author Tim Düsterhus
* @copyright 2010-2014 Tim Düsterhus
* @license Creative Commons Attribution-NonCommercial-ShareAlike <http://creativecommons.org/licenses/by-nc-sa/3.0/legalcode>
* @package be.bastelstu.chat
* @subpackage system.chat.command.commands
*/
class GbanCommand extends \chat\system\command\AbstractSuspensionCommand {
const IS_GLOBAL = true;
const SUSPENSION_TYPE = \chat\data\suspension\Suspension::TYPE_BAN;
}

View File

@ -0,0 +1,16 @@
<?php
namespace chat\system\command\commands;
/**
* Globally mutes a user.
*
* @author Tim Düsterhus
* @copyright 2010-2014 Tim Düsterhus
* @license Creative Commons Attribution-NonCommercial-ShareAlike <http://creativecommons.org/licenses/by-nc-sa/3.0/legalcode>
* @package be.bastelstu.chat
* @subpackage system.chat.command.commands
*/
class GmuteCommand extends \chat\system\command\AbstractSuspensionCommand {
const IS_GLOBAL = true;
const SUSPENSION_TYPE = \chat\data\suspension\Suspension::TYPE_MUTE;
}

View File

@ -0,0 +1,116 @@
<?php
namespace chat\system\command\commands;
use \chat\util\ChatUtil;
use \wcf\data\user\User;
use \wcf\system\WCF;
use \wcf\util\DateUtil;
/**
* Shows information about the specified user.
*
* @author Tim Düsterhus
* @copyright 2010-2014 Tim Düsterhus
* @license Creative Commons Attribution-NonCommercial-ShareAlike <http://creativecommons.org/licenses/by-nc-sa/3.0/legalcode>
* @package be.bastelstu.chat
* @subpackage system.chat.command.commands
*/
class InfoCommand extends \chat\system\command\AbstractCommand {
public $lines = array();
public $user = null;
public function __construct(\chat\system\command\CommandHandler $commandHandler) {
parent::__construct($commandHandler);
$username = rtrim($commandHandler->getParameters(), ',');
$this->user = User::getUserByUsername($username);
if (!$this->user->userID) throw new \chat\system\command\UserNotFoundException($username);
// Username + link to profile
$profile = \wcf\system\request\LinkHandler::getInstance()->getLink('User', array(
'object' => $this->user
));
$this->lines[WCF::getLanguage()->get('wcf.user.username')] = "[url='".$profile."']".$this->user->username.'[/url]';
// Away-Status
if ($this->user->chatAway !== null) {
$this->lines[WCF::getLanguage()->get('wcf.chat.away')] = $this->user->chatAway;
}
// Room
$room = \chat\data\room\RoomCache::getInstance()->getRoom($this->user->chatRoomID);
if ($room !== null && $room->canEnter()) {
$this->lines[WCF::getLanguage()->get('chat.global.room')] = $room->getTitle();
}
// Suspensions
$suspensions = \chat\data\suspension\Suspension::getSuspensionsForUser($this->user);
foreach ($suspensions as $roomSuspensions) {
foreach ($roomSuspensions as $typeSuspension) {
if (!$typeSuspension->isValid()) continue;
if (!$typeSuspension->isVisible()) continue;
$dateTime = DateUtil::getDateTimeByTimestamp($typeSuspension->expires);
$name = WCF::getLanguage()->getDynamicVariable('chat.global.information.suspension', array(
'suspension' => $typeSuspension,
'room' => \chat\data\room\RoomCache::getInstance()->getRoom($typeSuspension->roomID)
));
$this->lines[$name] = str_replace('%time%', DateUtil::format($dateTime, DateUtil::TIME_FORMAT), str_replace('%date%', DateUtil::format($dateTime, DateUtil::DATE_FORMAT), WCF::getLanguage()->get('wcf.date.dateTimeFormat')));
}
}
// ip address
if (WCF::getSession()->getPermission('admin.user.canViewIpAddress')) {
$session = $this->fetchSession();
if ($session) {
$this->lines[WCF::getLanguage()->get('wcf.user.ipAddress')] = \wcf\util\UserUtil::convertIPv6To4($session->ipAddress);
}
}
$this->didInit();
}
/**
* Fetches the sessiondatabase object for the specified user.
*
* @return \wcf\data\session\Session
*/
public function fetchSession() {
$sql = "SELECT
*
FROM
wcf".WCF_N."_session
WHERE
userID = ?";
$stmt = WCF::getDB()->prepareStatement($sql);
$stmt->execute(array($this->user->userID));
$row = $stmt->fetchArray();
if (!$row) return false;
return new \wcf\data\session\Session(null, $row);
}
/**
* @see \chat\system\command\ICommand::getType()
*/
public function getType() {
return \chat\data\message\Message::TYPE_INFORMATION;
}
/**
* @see \chat\system\command\ICommand::getMessage()
*/
public function getMessage() {
$lines = array();
foreach ($this->lines as $key => $val) {
$lines[] = '[b]'.$key.':[/b] '.$val;
}
return '[list][*]'.implode('[*]', $lines).'[/list]';
}
/**
* @see \chat\system\command\ICommand::getReceiver()
*/
public function getReceiver() {
return \wcf\system\WCF::getUser()->userID;
}
}

View File

@ -0,0 +1,76 @@
<?php
namespace chat\system\command\commands;
use \wcf\data\user\User;
use \wcf\system\WCF;
/**
* Invites a user into a temproom.
*
* @author Tim Düsterhus
* @copyright 2010-2014 Tim Düsterhus
* @license Creative Commons Attribution-NonCommercial-ShareAlike <http://creativecommons.org/licenses/by-nc-sa/3.0/legalcode>
* @package be.bastelstu.chat
* @subpackage system.chat.command.commands
*/
class InviteCommand extends \chat\system\command\AbstractRestrictedCommand {
public $user = null;
public $link = '';
public $room = null;
public function __construct(\chat\system\command\CommandHandler $commandHandler) {
parent::__construct($commandHandler);
$username = rtrim($commandHandler->getParameters(), ',');
$this->user = User::getUserByUsername($username);
if (!$this->user->userID) throw new \chat\system\command\UserNotFoundException($username);
$acl = \wcf\system\acl\ACLHandler::getInstance();
$permissions = $acl->getPermissions($acl->getObjectTypeID('be.bastelstu.chat.room'), array($this->room->roomID));
$newPermission = array();
foreach ($permissions['options'] as $option) {
$newPermission[$option->optionID] = ($option->categoryName == 'user') ? 1 : 0;
}
$_POST['aclValues'] = array(
'user' => $permissions['user'][$this->room->roomID],
'group' => $permissions['group'][$this->room->roomID]
);
$_POST['aclValues']['user'][$this->user->userID] = $newPermission;
$acl->save($this->room->roomID, $acl->getObjectTypeID('be.bastelstu.chat.room'));
\chat\system\permission\PermissionHandler::clearCache();
$this->didInit();
}
/**
* @see \chat\system\command\IRestrictedChatCommand::checkPermission()
*/
public function checkPermission() {
parent::checkPermission();
$this->room = $this->commandHandler->getRoom();
if ($this->room->owner != WCF::getUser()->userID) throw new \wcf\system\exception\PermissionDeniedException();
}
/**
* @see \chat\system\command\ICommand::getType()
*/
public function getType() {
return \chat\data\message\Message::TYPE_INFORMATION;
}
/**
* @see \chat\system\command\ICommand::getMessage()
*/
public function getMessage() {
return \wcf\system\WCF::getLanguage()->getDynamicVariable('chat.message.invite.success', array('user' => $this->user, 'room' => $this->room));
}
/**
* @see \chat\system\command\ICommand::getReceiver()
*/
public function getReceiver() {
return \wcf\system\WCF::getUser()->userID;
}
}

View File

@ -0,0 +1,38 @@
<?php
namespace chat\system\command\commands;
/**
* Indicates an action. The message is shown without the colon.
*
* @author Tim Düsterhus
* @copyright 2010-2014 Tim Düsterhus
* @license Creative Commons Attribution-NonCommercial-ShareAlike <http://creativecommons.org/licenses/by-nc-sa/3.0/legalcode>
* @package be.bastelstu.chat
* @subpackage system.chat.command.commands
*/
class MeCommand extends \chat\system\command\AbstractCommand {
/**
* @see \chat\system\command\AbstractCommand::$enableSmilies
*/
public $enableSmilies = self::SETTING_USER;
public function __construct(\chat\system\command\CommandHandler $commandHandler) {
parent::__construct($commandHandler);
$this->didInit();
}
/**
* @see \chat\system\command\ICommand::getType()
*/
public function getType() {
return \chat\data\message\Message::TYPE_ME;
}
/**
* @see \chat\system\command\ICommand::getMessage()
*/
public function getMessage() {
return $this->commandHandler->getParameters();
}
}

View File

@ -0,0 +1,16 @@
<?php
namespace chat\system\command\commands;
/**
* Mutes a user.
*
* @author Tim Düsterhus
* @copyright 2010-2014 Tim Düsterhus
* @license Creative Commons Attribution-NonCommercial-ShareAlike <http://creativecommons.org/licenses/by-nc-sa/3.0/legalcode>
* @package be.bastelstu.chat
* @subpackage system.chat.command.commands
*/
class MuteCommand extends \chat\system\command\AbstractSuspensionCommand {
const IS_GLOBAL = false;
const SUSPENSION_TYPE = \chat\data\suspension\Suspension::TYPE_MUTE;
}

View File

@ -0,0 +1,36 @@
<?php
namespace chat\system\command\commands;
/**
* Sends a message that starts with a slash.
*
* @author Tim Düsterhus
* @copyright 2010-2014 Tim Düsterhus
* @license Creative Commons Attribution-NonCommercial-ShareAlike <http://creativecommons.org/licenses/by-nc-sa/3.0/legalcode>
* @package be.bastelstu.chat
* @subpackage system.chat.command.commands
*/
class PlainCommand extends \chat\system\command\AbstractCommand {
public $enableSmilies = \chat\system\command\ICommand::SETTING_USER;
/**
* @see \chat\system\command\ICommand::getType()
*/
public function getType() {
return \chat\data\message\Message::TYPE_NORMAL;
}
/**
* @see \chat\system\command\ICommand::getMessage()
*/
public function getMessage() {
return \wcf\system\bbcode\PreParser::getInstance()->parse(\chat\system\command\CommandHandler::COMMAND_CHAR.$this->commandHandler->getParameters(), explode(',', \wcf\system\WCF::getSession()->getPermission('user.chat.allowedBBCodes')));
}
/**
* @see \chat\system\command\ICommand::getReceiver()
*/
public function getReceiver() {
return null;
}
}

View File

@ -0,0 +1,66 @@
<?php
namespace chat\system\command\commands;
use \wcf\data\user\User;
use \wcf\system\WCF;
use \wcf\util\ChatUtil;
/**
* Resets the color of a user
*
* @author Tim Düsterhus
* @copyright 2010-2014 Tim Düsterhus
* @license Creative Commons Attribution-NonCommercial-ShareAlike <http://creativecommons.org/licenses/by-nc-sa/3.0/legalcode>
* @package be.bastelstu.chat
* @subpackage system.chat.command.commands
*/
class RestoreCommand extends \chat\system\command\AbstractRestrictedCommand {
public $enableHTML = 1;
public $user = null;
public $link = '';
public function __construct(\chat\system\command\CommandHandler $commandHandler) {
parent::__construct($commandHandler);
$username = rtrim($commandHandler->getParameters(), ',');
$this->user = User::getUserByUsername($username);
if (!$this->user->userID) throw new \chat\system\command\UserNotFoundException($username);
$profile = \wcf\system\request\LinkHandler::getInstance()->getLink('User', array(
'object' => $this->user
));
$this->link = "[url='".$profile."']".$this->user->username.'[/url]';
$editor = new \wcf\data\user\UserEditor($this->user);
$editor->update(array(
'chatColor1' => 0,
'chatColor2' => 0
));
$this->didInit();
}
/**
* @see \chat\system\command\IRestrictedChatCommand::checkPermission()
*/
public function checkPermission() {
parent::checkPermission();
WCF::getSession()->checkPermissions(array('mod.chat.canRestore'));
}
/**
* @see \chat\system\command\ICommand::getType()
*/
public function getType() {
return \chat\data\message\Message::TYPE_MODERATE;
}
/**
* @see \chat\system\command\ICommand::getMessage()
*/
public function getMessage() {
return serialize(array(
'link' => $this->link,
'type' => str_replace(array('chat\system\command\commands\\', 'command'), '', strtolower(get_class($this)))
));
}
}

View File

@ -0,0 +1,97 @@
<?php
namespace chat\system\command\commands;
use \wcf\system\WCF;
/**
* Creates a temporary room
*
* @author Tim Düsterhus
* @copyright 2010-2014 Tim Düsterhus
* @license Creative Commons Attribution-NonCommercial-ShareAlike <http://creativecommons.org/licenses/by-nc-sa/3.0/legalcode>
* @package be.bastelstu.chat
* @subpackage system.chat.command.commands
*/
class TemproomCommand extends \chat\system\command\AbstractRestrictedCommand {
public $roomName = '';
public $roomID = 0;
public function __construct(\chat\system\command\CommandHandler $commandHandler) {
parent::__construct($commandHandler);
// create room
$this->objectAction = new \chat\data\room\RoomAction(array(), 'create', array('data' => array(
'title' => 'Temproom',
'topic' => '',
'permanent' => 0,
'owner' => WCF::getUser()->userID
)));
$this->objectAction->executeAction();
$returnValues = $this->objectAction->getReturnValues();
$chatRoomEditor = new \chat\data\room\RoomEditor($returnValues['returnValues']);
$this->roomID = $returnValues['returnValues']->roomID;
$this->roomName = WCF::getLanguage()->getDynamicVariable('chat.room.titleTemp', array('roomID' => $this->roomID));
// set accurate title
$chatRoomEditor->update(array(
'title' => $this->roomName
));
// set permissions
$acl = \wcf\system\acl\ACLHandler::getInstance();
$options = $acl->getOptions($acl->getObjectTypeID('be.bastelstu.chat.room'))->getObjects();
$_POST['aclValues'] = array(
'user' => array(
// creators may do everything
WCF::getUser()->userID => array_fill_keys(array_keys($options), 1)
),
'group' => array(
// anyone else may do nothing
\wcf\data\user\group\UserGroup::EVERYONE => array_fill_keys(array_keys($options), 0)
)
);
$acl->save($this->roomID, $acl->getObjectTypeID('be.bastelstu.chat.room'));
\chat\system\permission\PermissionHandler::clearCache();
$this->didInit();
}
/**
* @see \chat\system\command\IRestrictedCommand::checkPermission()
*/
public function checkPermission() {
parent::checkPermission();
WCF::getSession()->checkPermissions(array('user.chat.canTempRoom'));
}
/**
* @see \chat\system\command\ICommand::getType()
*/
public function getType() {
return \chat\data\message\Message::TYPE_INFORMATION;
}
/**
* @see \chat\system\command\ICommand::getMessage()
*/
public function getMessage() {
return WCF::getLanguage()->getDynamicVariable('chat.message.temproom.success', array('roomName' => $this->roomName));
}
/**
* @see \chat\system\command\ICommand::getReceiver()
*/
public function getReceiver() {
return \wcf\system\WCF::getUser()->userID;
}
/**
* @see \chat\system\command\ICommand::getAdditionalData()
*/
public function getAdditionalData() {
return array(
'roomID' => (int) $this->roomID,
'roomName' => $this->roomName
);
}
}

View File

@ -0,0 +1,16 @@
<?php
namespace chat\system\command\commands;
/**
* Bans a user.
*
* @author Tim Düsterhus
* @copyright 2010-2014 Tim Düsterhus
* @license Creative Commons Attribution-NonCommercial-ShareAlike <http://creativecommons.org/licenses/by-nc-sa/3.0/legalcode>
* @package be.bastelstu.chat
* @subpackage system.chat.command.commands
*/
class UnbanCommand extends \chat\system\command\AbstractUnsuspensionCommand {
const IS_GLOBAL = false;
const SUSPENSION_TYPE = \chat\data\suspension\Suspension::TYPE_BAN;
}

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