mirror of
https://github.com/openvk/openvk
synced 2025-04-23 00:23:01 +03:00
Merge branch 'master' into disco
This commit is contained in:
commit
de0155b31c
345 changed files with 18379 additions and 1959 deletions
15
.github/workflows/codeberg-mirror.yml
vendored
Normal file
15
.github/workflows/codeberg-mirror.yml
vendored
Normal file
|
@ -0,0 +1,15 @@
|
|||
name: Codeberg Mirroring
|
||||
|
||||
on: push
|
||||
|
||||
jobs:
|
||||
to_codeberg:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: pixta-dev/repository-mirroring-action@v1
|
||||
with:
|
||||
target_repo_url: "git@codeberg.org:openvk/openvk.git"
|
||||
ssh_private_key: ${{ secrets.CODEBERG_MIRRORSSH }}
|
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -14,3 +14,5 @@ themepacks/*
|
|||
!themepacks/midnight
|
||||
storage/*
|
||||
!storage/.gitkeep
|
||||
|
||||
.idea
|
8
.idea/.gitignore
vendored
8
.idea/.gitignore
vendored
|
@ -1,8 +0,0 @@
|
|||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# Editor-based HTTP Client requests
|
||||
/httpRequests/
|
||||
# Datasource local storage ignored files
|
||||
/dataSources/
|
||||
/dataSources.local.xml
|
|
@ -1,35 +0,0 @@
|
|||
<?xml encoding="UTF-8"?>
|
||||
<!ELEMENT latte (tags,filters,variables,functions)>
|
||||
<!ATTLIST latte vendor #REQUIRED>
|
||||
<!ATTLIST latte version #REQUIRED>
|
||||
<!ELEMENT tags (tag)+>
|
||||
<!ELEMENT tag (arguments)?>
|
||||
<!ATTLIST tag name CDATA #REQUIRED>
|
||||
<!ATTLIST tag type (PAIR|UNPAIRED|UNPAIRED_ATTR|ATTR_ONLY|AUTO_EMPTY) #REQUIRED>
|
||||
<!ATTLIST tag allowedFilters (true|false) #IMPLIED>
|
||||
<!ATTLIST tag arguments CDATA #IMPLIED>
|
||||
<!ATTLIST tag deprecatedMessage CDATA #IMPLIED>
|
||||
<!ATTLIST tag multiLine (true|false) #IMPLIED>
|
||||
<!ELEMENT arguments (argument)+>
|
||||
<!ELEMENT argument EMPTY>
|
||||
<!ATTLIST argument name #REQUIRED>
|
||||
<!ATTLIST argument types CDATA #REQUIRED>
|
||||
<!ATTLIST argument repeatable (true|false) #IMPLIED>
|
||||
<!ATTLIST argument required (true|false) #IMPLIED>
|
||||
<!ATTLIST argument validType #IMPLIED>
|
||||
<!ELEMENT filters (filter)+>
|
||||
<!ELEMENT filter EMPTY>
|
||||
<!ATTLIST filter name #REQUIRED>
|
||||
<!ATTLIST filter description CDATA #IMPLIED>
|
||||
<!ATTLIST filter arguments CDATA #IMPLIED>
|
||||
<!ATTLIST filter insertColons #IMPLIED>
|
||||
<!ELEMENT variables (variable)+>
|
||||
<!ELEMENT variable EMPTY>
|
||||
<!ATTLIST variable name #REQUIRED>
|
||||
<!ATTLIST variable type CDATA #REQUIRED>
|
||||
<!ELEMENT functions (function)+>
|
||||
<!ELEMENT function EMPTY>
|
||||
<!ATTLIST function name #REQUIRED>
|
||||
<!ATTLIST function arguments CDATA #REQUIRED>
|
||||
<!ATTLIST function returnType #REQUIRED>
|
||||
<!ATTLIST function description CDATA #IMPLIED>
|
|
@ -1,290 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE latte PUBLIC "-//LATTE//Latte plugin XML V0.0.1//EN" "Latte.dtd">
|
||||
<latte vendor="latte" version="1">
|
||||
<tags>
|
||||
<tag name="_" type="AUTO_EMPTY" allowedFilters="true">
|
||||
<arguments>
|
||||
<argument name="expression" types="PHP_EXPRESSION" validType="string" required="true" />
|
||||
</arguments>
|
||||
</tag>
|
||||
<tag name="=" type="UNPAIRED" allowedFilters="true">
|
||||
<arguments>
|
||||
<argument name="expression" types="PHP_EXPRESSION" validType="string" required="true" />
|
||||
</arguments>
|
||||
</tag>
|
||||
<tag name="block" type="AUTO_EMPTY" allowedFilters="true" multiLine="true">
|
||||
<arguments>
|
||||
<argument name="name" types="PHP_IDENTIFIER,VARIABLE,PHP_EXPRESSION" validType="string" required="true" />
|
||||
</arguments>
|
||||
</tag>
|
||||
<tag name="breakIf" type="UNPAIRED">
|
||||
<arguments>
|
||||
<argument name="condition" types="PHP_CONDITION" validType="bool" required="true" />
|
||||
</arguments>
|
||||
</tag>
|
||||
<tag name="capture" type="PAIR" allowedFilters="true" multiLine="true">
|
||||
<arguments>
|
||||
<argument name="variable" types="VARIABLE_DEFINITION" required="true" />
|
||||
</arguments>
|
||||
</tag>
|
||||
<tag name="case" type="UNPAIRED">
|
||||
<arguments>
|
||||
<argument name="condition" types="PHP_CONDITION" required="true" repeatable="true" />
|
||||
</arguments>
|
||||
</tag>
|
||||
<tag name="catch" type="UNPAIRED">
|
||||
<arguments>
|
||||
<argument name="condition" types="PHP_CONDITION" validType="bool" required="true" />
|
||||
</arguments>
|
||||
</tag>
|
||||
<tag name="contentType" type="UNPAIRED">
|
||||
<arguments>
|
||||
<argument name="content-type" types="CONTENT_TYPE" validType="string" required="true" />
|
||||
</arguments>
|
||||
</tag>
|
||||
<tag name="continueIf" type="UNPAIRED">
|
||||
<arguments>
|
||||
<argument name="condition" types="PHP_CONDITION" validType="bool" required="true" />
|
||||
</arguments>
|
||||
</tag>
|
||||
<tag name="debugbreak" type="UNPAIRED">
|
||||
<arguments>
|
||||
<argument name="expression" types="PHP_EXPRESSION" required="true" />
|
||||
</arguments>
|
||||
</tag>
|
||||
<tag name="default" type="UNPAIRED">
|
||||
<arguments>
|
||||
<argument name="variable" types="VARIABLE_DEFINITION_EXPRESSION" required="true" repeatable="true" />
|
||||
</arguments>
|
||||
</tag>
|
||||
<tag name="define" type="PAIR" multiLine="true">
|
||||
<arguments>
|
||||
<argument name="name" types="PHP_IDENTIFIER,VARIABLE,PHP_EXPRESSION" required="true" />
|
||||
<argument name="variable" types="VARIABLE_DEFINITION_ITEM" repeatable="true" />
|
||||
</arguments>
|
||||
</tag>
|
||||
<tag name="do" type="UNPAIRED">
|
||||
<arguments>
|
||||
<argument name="expression" types="PHP_EXPRESSION" required="true" />
|
||||
</arguments>
|
||||
</tag>
|
||||
<tag name="dump" type="UNPAIRED">
|
||||
<arguments>
|
||||
<argument name="expression" types="PHP_EXPRESSION" required="true" />
|
||||
</arguments>
|
||||
</tag>
|
||||
<tag name="else" type="UNPAIRED_ATTR" />
|
||||
<tag name="elseif" type="UNPAIRED">
|
||||
<arguments>
|
||||
<argument name="condition" types="PHP_CONDITION" validType="bool" required="true" />
|
||||
</arguments>
|
||||
</tag>
|
||||
<tag name="elseifset" type="UNPAIRED">
|
||||
<arguments>
|
||||
<argument name="var" types="VARIABLE,BLOCK" validType="string" required="true" />
|
||||
</arguments>
|
||||
</tag>
|
||||
<tag name="extends" type="UNPAIRED">
|
||||
<arguments>
|
||||
<argument name="file" types="PHP_IDENTIFIER,VARIABLE,PHP_EXPRESSION,NONE" validType="string" required="true" />
|
||||
</arguments>
|
||||
</tag>
|
||||
<tag name="first" type="PAIR">
|
||||
<arguments>
|
||||
<argument name="width" types="PHP_IDENTIFIER,PHP_EXPRESSION" validType="int" required="true" />
|
||||
</arguments>
|
||||
</tag>
|
||||
<tag name="for" type="PAIR" arguments="initialization; condition; afterthought" multiLine="true" />
|
||||
<tag name="foreach" type="PAIR" arguments="expression as [$key =>] $value" allowedFilters="true" multiLine="true" />
|
||||
<tag name="if" type="PAIR">
|
||||
<arguments>
|
||||
<argument name="condition" types="PHP_CONDITION" validType="bool" required="true" />
|
||||
</arguments>
|
||||
</tag>
|
||||
<tag name="ifset" type="PAIR">
|
||||
<arguments>
|
||||
<argument name="var" types="VARIABLE,BLOCK,PHP_EXPRESSION" validType="string" required="true" />
|
||||
</arguments>
|
||||
</tag>
|
||||
<tag name="import" type="UNPAIRED">
|
||||
<arguments>
|
||||
<argument name="file" types="PHP_IDENTIFIER,VARIABLE,PHP_EXPRESSION" validType="string" required="true" />
|
||||
</arguments>
|
||||
</tag>
|
||||
<tag name="include" type="UNPAIRED" allowedFilters="true">
|
||||
<arguments>
|
||||
<argument name="file" types="BLOCK,IDENTIFIER,PHP_EXPRESSION" validType="string" required="true" />
|
||||
<argument name="arguments" types="KEY_VALUE" repeatable="true" />
|
||||
</arguments>
|
||||
</tag>
|
||||
<tag name="includeblock" type="UNPAIRED">
|
||||
<arguments>
|
||||
<argument name="file" types="PHP_IDENTIFIER,VARIABLE,PHP_EXPRESSION" validType="string" required="true" />
|
||||
</arguments>
|
||||
</tag>
|
||||
<tag name="l" type="UNPAIRED" />
|
||||
<tag name="last" type="PAIR">
|
||||
<arguments>
|
||||
<argument name="width" types="PHP_IDENTIFIER,PHP_EXPRESSION" validType="int" required="true" />
|
||||
</arguments>
|
||||
</tag>
|
||||
<tag name="layout" type="UNPAIRED">
|
||||
<arguments>
|
||||
<argument name="file" types="PHP_IDENTIFIER,VARIABLE,PHP_EXPRESSION,NONE" validType="string" required="true" />
|
||||
</arguments>
|
||||
</tag>
|
||||
<tag name="class" type="ATTR_ONLY" arguments="class" />
|
||||
<tag name="attr" type="ATTR_ONLY" arguments="attr" />
|
||||
<tag name="ifcontent" type="ATTR_ONLY" />
|
||||
<tag name="php" type="UNPAIRED">
|
||||
<arguments>
|
||||
<argument name="expression" types="PHP_EXPRESSION" required="true" />
|
||||
</arguments>
|
||||
</tag>
|
||||
<tag name="r" type="UNPAIRED" />
|
||||
<tag name="sandbox" type="UNPAIRED">
|
||||
<arguments>
|
||||
<argument name="file" types="BLOCK,PHP_IDENTIFIER,VARIABLE,PHP_EXPRESSION" validType="string" required="true" />
|
||||
<argument name="key-value" types="KEY_VALUE" repeatable="true" />
|
||||
</arguments>
|
||||
</tag>
|
||||
<tag name="sep" type="PAIR">
|
||||
<arguments>
|
||||
<argument name="width" types="PHP_IDENTIFIER,PHP_EXPRESSION" validType="int" />
|
||||
</arguments>
|
||||
</tag>
|
||||
<tag name="snippet" type="PAIR" multiLine="true">
|
||||
<arguments>
|
||||
<argument name="name" types="PHP_IDENTIFIER,VARIABLE,PHP_EXPRESSION" validType="string" />
|
||||
</arguments>
|
||||
</tag>
|
||||
<tag name="snippetArea" type="PAIR" multiLine="true">
|
||||
<arguments>
|
||||
<argument name="name" types="PHP_IDENTIFIER,PHP_EXPRESSION" validType="string" required="true" />
|
||||
</arguments>
|
||||
</tag>
|
||||
<tag name="spaceless" type="PAIR" />
|
||||
<tag name="switch" type="PAIR" multiLine="true">
|
||||
<arguments>
|
||||
<argument name="expression" types="PHP_EXPRESSION" />
|
||||
</arguments>
|
||||
</tag>
|
||||
<tag name="syntax" type="PAIR" arguments="off | double | latte" multiLine="true" />
|
||||
<tag name="templatePrint" type="UNPAIRED">
|
||||
<arguments>
|
||||
<argument name="class-name" types="PHP_CLASS_NAME" />
|
||||
</arguments>
|
||||
</tag>
|
||||
<tag name="templateType" type="UNPAIRED">
|
||||
<arguments>
|
||||
<argument name="class-name" types="PHP_CLASS_NAME" required="true" />
|
||||
</arguments>
|
||||
</tag>
|
||||
<tag name="try" type="PAIR" />
|
||||
<tag name="rollback" type="UNPAIRED" />
|
||||
<tag name="tag" type="ATTR_ONLY">
|
||||
<arguments>
|
||||
<argument name="expression" types="PHP_EXPRESSION" required="true" validType="string" repeatable="true" />
|
||||
</arguments>
|
||||
</tag>
|
||||
<tag name="ifchanged" type="PAIR">
|
||||
<arguments>
|
||||
<argument name="expression" types="PHP_EXPRESSION" required="true" repeatable="true" />
|
||||
</arguments>
|
||||
</tag>
|
||||
<tag name="skipIf" type="UNPAIRED">
|
||||
<arguments>
|
||||
<argument name="condition" types="PHP_CONDITION" validType="bool" required="true" />
|
||||
</arguments>
|
||||
</tag>
|
||||
<tag name="var" type="UNPAIRED">
|
||||
<arguments>
|
||||
<argument name="variable" types="VARIABLE_DEFINITION_EXPRESSION" required="true" repeatable="true" />
|
||||
</arguments>
|
||||
</tag>
|
||||
<tag name="trace" type="UNPAIRED" />
|
||||
<tag name="varPrint" type="UNPAIRED" arguments="all" />
|
||||
<tag name="varType" type="UNPAIRED">
|
||||
<arguments>
|
||||
<argument name="file" types="PHP_TYPE" required="true" />
|
||||
<argument name="variable" types="VARIABLE_DEFINITION" required="true" />
|
||||
</arguments>
|
||||
</tag>
|
||||
<tag name="while" type="PAIR" multiLine="true">
|
||||
<arguments>
|
||||
<argument name="condition" types="PHP_CONDITION" validType="bool" required="true" />
|
||||
</arguments>
|
||||
</tag>
|
||||
<tag name="iterateWhile" type="PAIR" multiLine="true" />
|
||||
<tag name="embed" type="PAIR" multiLine="true">
|
||||
<arguments>
|
||||
<argument name="file" types="BLOCK_USAGE,PHP_IDENTIFIER,VARIABLE,PHP_EXPRESSION" validType="string" required="true" />
|
||||
<argument name="key-value" types="KEY_VALUE" repeatable="true" />
|
||||
</arguments>
|
||||
</tag>
|
||||
<!-- @deprecated - latte -->
|
||||
<tag name="assign" type="UNPAIRED" arguments="$variable = expr" />
|
||||
<tag name="truncate" type="UNPAIRED" arguments="expression" deprecatedMessage="Tag {? ...} is deprecated in Latte 2.4. For variable definitions use {var ...} or {php ...} in other cases." />
|
||||
</tags>
|
||||
<filters>
|
||||
<filter name="truncate" arguments=":($length, $append = '…')" description="shortens the length preserving whole words" insertColons=":" />
|
||||
<filter name="substr" arguments=":($offset [, $length])" description="returns part of the string" insertColons=":" />
|
||||
<filter name="trim" arguments=":($charset = mezery)" description="strips whitespace or other characters from the beginning and end of the string" />
|
||||
<filter name="stripHtml" arguments="" description="removes HTML tags and converts HTML entities to text" />
|
||||
<filter name="strip" arguments="" description="removes whitespace" />
|
||||
<filter name="indent" arguments=":($level = 1, $char = '\t')" description="indents the text from left with number of tabs" />
|
||||
<filter name="replace" arguments=":($search, $replace = '')" description="replaces all occurrences of the search string with the replacement" insertColons=":" />
|
||||
<filter name="replaceRE" arguments=":($pattern, $replace = '')" description="replaces all occurrences according to regular expression" insertColons=":" />
|
||||
<filter name="padLeft" arguments=":($length, $pad = ' ')" description="completes the string to given length from left" insertColons=":" />
|
||||
<filter name="padRight" arguments=":($length, $pad = ' ')" description="completes the string to given length from right" insertColons=":" />
|
||||
<filter name="repeat" arguments=":($count)" description="repeats the string" insertColons=":" />
|
||||
<filter name="implode" arguments=":($glue = '')" description="joins an array to a string" />
|
||||
<filter name="webalize" description="adjusts the UTF-8 string to the shape used in the URL" />
|
||||
<filter name="breaklines" description="inserts HTML line breaks before all newlines" />
|
||||
<filter name="reverse" description="reverse an UTF-8 string or array" />
|
||||
<filter name="length" description="returns length of a string or array" />
|
||||
<filter name="sort" description="simply sorts array" />
|
||||
<filter name="reverse" description="array sorted in reverse order (used with |sort)" />
|
||||
<filter name="batch" arguments=":($array, $length [, $item])" description="returns length of a string or array" insertColons="::" />
|
||||
|
||||
<filter name="clamp" description="returns value clamped to the inclusive range of min and max." insertColons="::" />
|
||||
|
||||
<filter name="lower" description="makes a string lower case" />
|
||||
<filter name="upper" description="makes a string upper case" />
|
||||
<filter name="firstUpper" description="makes the first letter upper case" />
|
||||
<filter name="capitalize" description="lower case, the first letter of each word upper case" />
|
||||
|
||||
<filter name="date" arguments=":($format)" description="formats date" insertColons=":" />
|
||||
<filter name="number" arguments=":($decimals = 0, $decPoint = '.', $thousandsSep = ',')" description="format number" />
|
||||
<filter name="bytes" arguments=":($precision = 2)" description="formats size in bytes" />
|
||||
<filter name="dataStream" arguments=":($mimetype = 'detect')" description="Data URI protocol conversion" />
|
||||
|
||||
<filter name="noescape" description="prints a variable without escaping" />
|
||||
<filter name="escapeurl" description="escapes parameter in URL" />
|
||||
|
||||
<filter name="nocheck" description="prevents automatic URL sanitization" />
|
||||
<filter name="checkurl" description="sanitizes string for use inside href attribute" />
|
||||
|
||||
<filter name="query" description="generates a query string in the URL" />
|
||||
<filter name="ceil" arguments=":(int $precision = 0)" description="rounds a number up to a given precision" />
|
||||
<filter name="explode" arguments=":(string $separator = '')" description="splits a string by the given delimiter" />
|
||||
<filter name="first" description="returns first element of array or character of string" />
|
||||
<filter name="floor" arguments=":(int $precision = 0)" description="rounds a number down to a given precision" />
|
||||
<filter name="join" arguments=":(string $glue = '')" description="joins an array to a string" />
|
||||
<filter name="last" description="returns last element of array or character of string" />
|
||||
<filter name="random" description="returns random element of array or character of string" />
|
||||
<filter name="round" arguments=":(int $precision = 0)" description="rounds a number to a given precision" />
|
||||
<filter name="slice" arguments=":(int $start, int $length = null, bool $preserveKeys = false)" description="extracts a slice of an array or a string" insertColons=":" />
|
||||
<filter name="spaceless" description="removes whitespace" />
|
||||
<filter name="split" arguments=":(string $separator = '')" description="splits a string by the given delimiter" />
|
||||
</filters>
|
||||
<functions>
|
||||
<function name="clamp" returnType="int|float" arguments="(int|float $value, int|float $min, int|float $max)" description="clamps value to the inclusive range of min and max" />
|
||||
<function name="divisibleBy" returnType="bool" arguments="(int $value)" description="checks if a variable is divisible by a number" />
|
||||
<function name="even" returnType="bool" arguments="(int $value)" description="checks if the given number is even" />
|
||||
<function name="first" returnType="mixed" arguments="(string|array $value)" description="returns first element of array or character of string" />
|
||||
<function name="last" returnType="mixed" arguments="(string|array $value)" description="returns last element of array or character of string" />
|
||||
<function name="odd" returnType="bool" arguments="(int $value)" description="checks if the given number is odd" />
|
||||
<function name="slice" returnType="string|array" arguments="(string|array $value, int $start, int $length = null, bool $preserveKeys = false)" description="extracts a slice of an array or a string" />
|
||||
</functions>
|
||||
</latte>
|
|
@ -1,59 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE latte PUBLIC "-//LATTE//Latte plugin configuration XML V0.0.1//EN" "Latte.dtd">
|
||||
<latte version="1" vendor="nette/application">
|
||||
<tags>
|
||||
<!-- nette/application tags -->
|
||||
<tag name="cache" type="PAIR" arguments="if => expr, key, …">
|
||||
<arguments>
|
||||
<argument name="name[:part]" types="KEY_VALUE" validType="string" required="true" />
|
||||
<argument name="arguments" types="PHP_EXPRESSION" repeatable="true" />
|
||||
</arguments>
|
||||
</tag>
|
||||
<tag name="control" type="UNPAIRED">
|
||||
<arguments>
|
||||
<argument name="name[:part]" types="PHP_IDENTIFIER,PHP_EXPRESSION" validType="string" required="true" />
|
||||
<argument name="arguments" types="PHP_EXPRESSION" repeatable="true" />
|
||||
</arguments>
|
||||
</tag>
|
||||
<tag name="link" type="UNPAIRED">
|
||||
<arguments>
|
||||
<argument name="destination" types="LINK_DESTINATION,PHP_EXPRESSION" validType="string" required="true" />
|
||||
<argument name="arguments" types="LINK_PARAMETERS" repeatable="true" />
|
||||
</arguments>
|
||||
</tag>
|
||||
<tag name="href" type="ATTR_ONLY">
|
||||
<arguments>
|
||||
<argument name="destination" types="LINK_DESTINATION,PHP_EXPRESSION" validType="string" required="true" />
|
||||
<argument name="arguments" types="LINK_PARAMETERS" repeatable="true" />
|
||||
</arguments>
|
||||
</tag>
|
||||
<tag name="nonce" type="ATTR_ONLY" />
|
||||
<tag name="plink" type="UNPAIRED">
|
||||
<arguments>
|
||||
<argument name="destination" types="LINK_DESTINATION,PHP_EXPRESSION" validType="string" required="true" />
|
||||
<argument name="arguments" types="LINK_PARAMETERS" repeatable="true" />
|
||||
</arguments>
|
||||
</tag>
|
||||
<!-- @deprecated - nette/application -->
|
||||
<tag name="ifCurrent" type="PAIR" deprecatedMessage="Tag {ifCurrent} is deprecated in Latte 2.6. Use custom function isLinkCurrent() instead.">
|
||||
<arguments>
|
||||
<argument name="destination" types="LINK_DESTINATION,PHP_EXPRESSION" validType="string" required="true" />
|
||||
<argument name="arguments" types="LINK_PARAMETERS" repeatable="true" />
|
||||
</arguments>
|
||||
</tag>
|
||||
</tags>
|
||||
<variables>
|
||||
<variable name="control" type="\Nette\Application\UI\Control" />
|
||||
<variable name="basePath" type="string" />
|
||||
<variable name="baseUrl" type="string" />
|
||||
<variable name="flashes" type="mixed[]" />
|
||||
<variable name="presenter" type="\Nette\Application\UI\Presenter" />
|
||||
<variable name="iterator" type="\Latte\Runtime\CachingIterator" />
|
||||
<variable name="form" type="\Nette\Application\UI\Form" />
|
||||
<variable name="user" type="\Nette\Security\User" />
|
||||
</variables>
|
||||
<functions>
|
||||
<function name="isLinkCurrent" returnType="bool" arguments="(string $destination = null, $args = [])" />
|
||||
<function name="isModuleCurrent" returnType="bool" arguments="(string $moduleName)" />
|
||||
</functions>
|
||||
</latte>
|
|
@ -1,41 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE latte PUBLIC "-//LATTE//Latte plugin configuration XML V0.0.1//EN" "Latte.dtd">
|
||||
<latte version="1" vendor="nette/forms">
|
||||
<tags>
|
||||
<tag name="form" type="PAIR" multiLine="true">
|
||||
<arguments>
|
||||
<argument name="name" types="PHP_IDENTIFIER,VARIABLE,PHP_EXPRESSION" validType="string" required="true" />
|
||||
</arguments>
|
||||
</tag>
|
||||
<tag name="formContainer" type="PAIR" multiLine="true">
|
||||
<arguments>
|
||||
<argument name="name" types="PHP_IDENTIFIER,VARIABLE,PHP_EXPRESSION" validType="string" required="true" />
|
||||
</arguments>
|
||||
</tag>
|
||||
<tag name="formPrint" type="UNPAIRED">
|
||||
<arguments>
|
||||
<argument name="name" types="PHP_IDENTIFIER,VARIABLE,PHP_EXPRESSION" validType="string" required="true" />
|
||||
</arguments>
|
||||
</tag>
|
||||
<tag name="input" type="UNPAIRED">
|
||||
<arguments>
|
||||
<argument name="name" types="PHP_IDENTIFIER,VARIABLE,CONTROL,PHP_EXPRESSION" validType="string" required="true" />
|
||||
</arguments>
|
||||
</tag>
|
||||
<tag name="inputError" type="UNPAIRED">
|
||||
<arguments>
|
||||
<argument name="name" types="PHP_IDENTIFIER,VARIABLE,CONTROL,PHP_EXPRESSION" validType="string" required="true" />
|
||||
</arguments>
|
||||
</tag>
|
||||
<tag name="label" type="AUTO_EMPTY">
|
||||
<arguments>
|
||||
<argument name="name" types="PHP_IDENTIFIER,VARIABLE,CONTROL,PHP_EXPRESSION" validType="string" required="true" />
|
||||
</arguments>
|
||||
</tag>
|
||||
<tag name="name" type="ATTR_ONLY">
|
||||
<arguments>
|
||||
<argument name="name" types="PHP_IDENTIFIER,VARIABLE,CONTROL,PHP_EXPRESSION" validType="string" required="true" />
|
||||
</arguments>
|
||||
</tag>
|
||||
</tags>
|
||||
</latte>
|
|
@ -1,6 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectRootManager">
|
||||
<output url="file://$PROJECT_DIR$/out" />
|
||||
</component>
|
||||
</project>
|
|
@ -1,8 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/openvk.iml" filepath="$PROJECT_DIR$/.idea/openvk.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
|
@ -1,46 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="JAVA_MODULE" version="4">
|
||||
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/rybakit/msgpack" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/chillerlan/php-qrcode" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/psr/cache" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/chillerlan/php-settings-container" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/vearutop/php-obscene-censor-rus" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/psr/http-message" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/polyfill-php72" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/polyfill-intl-idn" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/polyfill-intl-normalizer" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/scssphp/scssphp" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/bhaktaraz/php-rss-generator" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/erusev/parsedown" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/ezyang/htmlpurifier" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/whichbrowser/parser" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/komeiji-satori/curl" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/composer" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/james-heinrich/getid3" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/guzzlehttp/promises" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/ralouphie/getallheaders" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/wapmorgan/binary-stream" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/guzzlehttp/psr7" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/al/emoji-detector" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/guzzlehttp/guzzle" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/lfkeitel/phptotp" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/zadarma/user-api-v1" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/polyfill-ctype" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/string" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/polyfill-intl-grapheme" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/deprecation-contracts" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/polyfill-php73" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/psr/container" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/polyfill-mbstring" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/console" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/polyfill-php80" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/service-contracts" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/wapmorgan/morphos" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
|
@ -1,48 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="PhpIncludePathManager">
|
||||
<include_path>
|
||||
<path value="$PROJECT_DIR$/vendor/rybakit/msgpack" />
|
||||
<path value="$PROJECT_DIR$/vendor/chillerlan/php-qrcode" />
|
||||
<path value="$PROJECT_DIR$/vendor/psr/cache" />
|
||||
<path value="$PROJECT_DIR$/vendor/chillerlan/php-settings-container" />
|
||||
<path value="$PROJECT_DIR$/vendor/vearutop/php-obscene-censor-rus" />
|
||||
<path value="$PROJECT_DIR$/vendor/psr/http-message" />
|
||||
<path value="$PROJECT_DIR$/vendor/symfony/polyfill-php72" />
|
||||
<path value="$PROJECT_DIR$/vendor/symfony/polyfill-intl-idn" />
|
||||
<path value="$PROJECT_DIR$/vendor/symfony/polyfill-intl-normalizer" />
|
||||
<path value="$PROJECT_DIR$/vendor/scssphp/scssphp" />
|
||||
<path value="$PROJECT_DIR$/vendor/bhaktaraz/php-rss-generator" />
|
||||
<path value="$PROJECT_DIR$/vendor/erusev/parsedown" />
|
||||
<path value="$PROJECT_DIR$/vendor/ezyang/htmlpurifier" />
|
||||
<path value="$PROJECT_DIR$/vendor/whichbrowser/parser" />
|
||||
<path value="$PROJECT_DIR$/vendor/komeiji-satori/curl" />
|
||||
<path value="$PROJECT_DIR$/vendor/composer" />
|
||||
<path value="$PROJECT_DIR$/vendor/james-heinrich/getid3" />
|
||||
<path value="$PROJECT_DIR$/vendor/guzzlehttp/promises" />
|
||||
<path value="$PROJECT_DIR$/vendor/ralouphie/getallheaders" />
|
||||
<path value="$PROJECT_DIR$/vendor/wapmorgan/binary-stream" />
|
||||
<path value="$PROJECT_DIR$/vendor/guzzlehttp/psr7" />
|
||||
<path value="$PROJECT_DIR$/vendor/al/emoji-detector" />
|
||||
<path value="$PROJECT_DIR$/vendor/guzzlehttp/guzzle" />
|
||||
<path value="$PROJECT_DIR$/vendor/lfkeitel/phptotp" />
|
||||
<path value="$PROJECT_DIR$/vendor/zadarma/user-api-v1" />
|
||||
<path value="$PROJECT_DIR$/../../../chandler" />
|
||||
<path value="$PROJECT_DIR$/../../../vendor" />
|
||||
<path value="$PROJECT_DIR$/vendor/symfony/polyfill-ctype" />
|
||||
<path value="$PROJECT_DIR$/vendor/symfony/string" />
|
||||
<path value="$PROJECT_DIR$/vendor/symfony/polyfill-intl-grapheme" />
|
||||
<path value="$PROJECT_DIR$/vendor/symfony/deprecation-contracts" />
|
||||
<path value="$PROJECT_DIR$/vendor/symfony/polyfill-php73" />
|
||||
<path value="$PROJECT_DIR$/vendor/psr/container" />
|
||||
<path value="$PROJECT_DIR$/vendor/symfony/polyfill-mbstring" />
|
||||
<path value="$PROJECT_DIR$/vendor/symfony/console" />
|
||||
<path value="$PROJECT_DIR$/vendor/symfony/polyfill-php80" />
|
||||
<path value="$PROJECT_DIR$/vendor/symfony/service-contracts" />
|
||||
<path value="$PROJECT_DIR$/vendor/wapmorgan/morphos" />
|
||||
</include_path>
|
||||
</component>
|
||||
<component name="PhpProjectSharedConfiguration" php_language_level="7.4">
|
||||
<option name="suggestChangeDefaultLanguageLevel" value="false" />
|
||||
</component>
|
||||
</project>
|
|
@ -1,10 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="RunConfigurationProducerService">
|
||||
<option name="ignoredProducers">
|
||||
<set>
|
||||
<option value="com.android.tools.idea.compose.preview.runconfiguration.ComposePreviewRunConfigurationProducer" />
|
||||
</set>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
|
@ -1,6 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
140
DBEntity.updated.php
Normal file
140
DBEntity.updated.php
Normal file
|
@ -0,0 +1,140 @@
|
|||
<?php declare(strict_types=1);
|
||||
namespace Chandler\Database;
|
||||
use Chandler\Database\DatabaseConnection;
|
||||
use Nette\Database\Table\Selection;
|
||||
use Nette\Database\Table\ActiveRow;
|
||||
use Nette\InvalidStateException as ISE;
|
||||
use openvk\Web\Models\Repositories\CurrentUser;
|
||||
use openvk\Web\Models\Repositories\Logs;
|
||||
|
||||
|
||||
abstract class DBEntity
|
||||
{
|
||||
protected $record;
|
||||
protected $changes;
|
||||
protected $deleted;
|
||||
protected $user;
|
||||
|
||||
protected $tableName;
|
||||
|
||||
function __construct(?ActiveRow $row = NULL)
|
||||
{
|
||||
if(is_null($row)) return;
|
||||
|
||||
$_table = $row->getTable()->getName();
|
||||
if($_table !== $this->tableName)
|
||||
throw new ISE("Invalid data supplied for model: table $_table is not compatible with table" . $this->tableName);
|
||||
|
||||
$this->record = $row;
|
||||
}
|
||||
|
||||
function __call(string $fName, array $args)
|
||||
{
|
||||
if(substr($fName, 0, 3) === "set") {
|
||||
$field = mb_strtolower(substr($fName, 3));
|
||||
$this->stateChanges($field, $args[0]);
|
||||
} else {
|
||||
throw new \Error("Call to undefined method " . get_class($this) . "::$fName");
|
||||
}
|
||||
}
|
||||
|
||||
private function getTable(): Selection
|
||||
{
|
||||
return DatabaseConnection::i()->getContext()->table($this->tableName);
|
||||
}
|
||||
|
||||
protected function getRecord(): ?ActiveRow
|
||||
{
|
||||
return $this->record;
|
||||
}
|
||||
|
||||
protected function stateChanges(string $column, $value): void
|
||||
{
|
||||
if(!is_null($this->record))
|
||||
$t = $this->record->{$column}; #Test if column exists
|
||||
|
||||
$this->changes[$column] = $value;
|
||||
}
|
||||
|
||||
function getId()
|
||||
{
|
||||
return $this->getRecord()->id;
|
||||
}
|
||||
|
||||
function isDeleted(): bool
|
||||
{
|
||||
return (bool) $this->getRecord()->deleted;
|
||||
}
|
||||
|
||||
function unwrap(): object
|
||||
{
|
||||
return (object) $this->getRecord()->toArray();
|
||||
}
|
||||
|
||||
function delete(bool $softly = true): void
|
||||
{
|
||||
$user = CurrentUser::i()->getUser();
|
||||
$user_id = is_null($user) ? (int) OPENVK_ROOT_CONF["openvk"]["preferences"]["support"]["adminAccount"] : $user->getId();
|
||||
|
||||
if(is_null($this->record))
|
||||
throw new ISE("Can't delete a model, that hasn't been flushed to DB. Have you forgotten to call save() first?");
|
||||
|
||||
(new Logs)->create($user_id, $this->getTable()->getName(), get_class($this), 2, $this->record->toArray(), $this->changes);
|
||||
|
||||
if($softly) {
|
||||
$this->record = $this->getTable()->where("id", $this->record->id)->update(["deleted" => true]);
|
||||
} else {
|
||||
$this->record->delete();
|
||||
$this->deleted = true;
|
||||
}
|
||||
}
|
||||
|
||||
function undelete(): void
|
||||
{
|
||||
if(is_null($this->record))
|
||||
throw new ISE("Can't undelete a model, that hasn't been flushed to DB. Have you forgotten to call save() first?");
|
||||
|
||||
$user = CurrentUser::i()->getUser();
|
||||
$user_id = is_null($user) ? (int) OPENVK_ROOT_CONF["openvk"]["preferences"]["support"]["adminAccount"] : $user->getId();
|
||||
|
||||
(new Logs)->create($user_id, $this->getTable()->getName(), get_class($this), 3, $this->record->toArray(), ["deleted" => false]);
|
||||
|
||||
$this->getTable()->where("id", $this->record->id)->update(["deleted" => false]);
|
||||
}
|
||||
|
||||
function save(?bool $log = true): void
|
||||
{
|
||||
if ($log) {
|
||||
$user = CurrentUser::i();
|
||||
$user_id = is_null($user) ? (int)OPENVK_ROOT_CONF["openvk"]["preferences"]["support"]["adminAccount"] : $user->getUser()->getId();
|
||||
}
|
||||
|
||||
if(is_null($this->record)) {
|
||||
$this->record = $this->getTable()->insert($this->changes);
|
||||
|
||||
if ($log && $this->getTable()->getName() !== "logs") {
|
||||
(new Logs)->create($user_id, $this->getTable()->getName(), get_class($this), 0, $this->record->toArray(), $this->changes);
|
||||
}
|
||||
} else {
|
||||
if ($log && $this->getTable()->getName() !== "logs") {
|
||||
(new Logs)->create($user_id, $this->getTable()->getName(), get_class($this), 1, $this->record->toArray(), $this->changes);
|
||||
}
|
||||
|
||||
if ($this->deleted) {
|
||||
$this->record = $this->getTable()->insert((array)$this->record);
|
||||
} else {
|
||||
$this->getTable()->get($this->record->id)->update($this->changes);
|
||||
$this->record = $this->getTable()->get($this->record->id);
|
||||
}
|
||||
}
|
||||
|
||||
$this->changes = [];
|
||||
}
|
||||
|
||||
function getTableName(): string
|
||||
{
|
||||
return $this->getTable()->getName();
|
||||
}
|
||||
|
||||
use \Nette\SmartObject;
|
||||
}
|
36
README.md
36
README.md
|
@ -2,29 +2,25 @@
|
|||
|
||||
_[Русский](README_RU.md)_
|
||||
|
||||
**OpenVK** is an attempt to create a simple CMS that ~~cosplays~~ imitates old VK. Code provided here is not stable yet.
|
||||
**OpenVK** is an attempt to create a simple CMS that ~~cosplays~~ imitates old VKontakte. Code provided here is not stable yet.
|
||||
|
||||
VKontakte belongs to Pavel Durov and VK Group.
|
||||
|
||||
To be honest, we don't know whether it even works. However, this version is maintained and we will be happy to accept your bugreports [in our bug-tracker](https://github.com/openvk/openvk/projects/1). You should also be able to submit them using [ticketing system](https://openvk.su/support?act=new) (you will need an OVK account for this).
|
||||
To be honest, we don't know whether if it even works. However, this version is maintained and we will be happy to accept your bugreports [in our bug tracker](https://github.com/openvk/openvk/projects/1). You should also be able to submit them using [ticketing system](https://openvk.su/support?act=new) (you will need an OpenVK account for this).
|
||||
|
||||
## When's the release?
|
||||
|
||||
We will release OpenVK as soon as it's ready. As for now you can:
|
||||
We will release OpenVK as soon as it's ready. As for now, you can:
|
||||
* `git clone` this repo's master branch (use `git pull` to update)
|
||||
* Grab a prebuilt OpenVK distro from [GitHub artifacts](https://nightly.link/openvk/archive/workflows/nightly/master/OpenVK%20Archive.zip)
|
||||
|
||||
## Instances
|
||||
|
||||
* **[openvk.su](https://openvk.su/)**
|
||||
* **[openvk.uk](https://openvk.uk)** - official mirror of openvk.su (<https://t.me/openvk/1609>)
|
||||
* **[openvk.co](http://openvk.co)** - yet another official mirror of openvk.su without TLS (<https://t.me/openvk/1654>)
|
||||
* [social.fetbuk.ru](http://social.fetbuk.ru/)
|
||||
* [vepurovk.xyz](http://vepurovk.xyz/)
|
||||
A list of instances can be found in [our wiki of this repository](https://github.com/openvk/openvk/wiki/Instances).
|
||||
|
||||
## Can I create my own OpenVK instance?
|
||||
|
||||
Yes! And you're very welcome to.
|
||||
Yes! And you are very welcome to.
|
||||
|
||||
However, OVK makes use of Chandler Application Server. This software requires extensions, that may not be provided by your hosting provider (namely, sodium and yaml. these extensions are available on most of ISPManager hostings).
|
||||
|
||||
|
@ -34,12 +30,12 @@ If you want, you can add your instance to the list above so that people can regi
|
|||
|
||||
1. Install PHP 7.4, web-server, Composer, Node.js, Yarn and [Chandler](https://github.com/openvk/chandler)
|
||||
|
||||
* PHP 8.1 is supported, but it was not tested carefully, be aware of that.
|
||||
* PHP 8.1 is supported too, however it was not tested carefully, so be aware.
|
||||
|
||||
2. Install MySQL-compatible database.
|
||||
|
||||
* We recommend using Percona Server, but any MySQL-compatible server should work
|
||||
* Server should be compatible with at least MySQL 5.6, MySQL 8.0+ recommended.
|
||||
* We recommend using Percona Server, but any MySQL-compatible server should work too.
|
||||
* Server should be compatible with at least MySQL 5.6, MySQL 8.0+ is recommended.
|
||||
* Support for MySQL 4.1+ is WIP, replace `utf8mb4` and `utf8mb4_unicode_520_ci` with `utf8` and `utf8_unicode_ci` in SQLs.
|
||||
|
||||
3. Install [commitcaptcha](https://github.com/openvk/commitcaptcha) and OpenVK as Chandler extensions like this:
|
||||
|
@ -70,27 +66,27 @@ Once you are done, you can login as a system administrator on the network itself
|
|||
* **Password**: `admin`
|
||||
* It is recommended to change the password of the built-in account or disable it.
|
||||
|
||||
💡Confused? Full installation walkthrough is available [here](https://docs.openvk.su/openvk_engine/centos8_installation/) (CentOS 8 [and](https://almalinux.org/) [family](https://yum.oracle.com/oracle-linux-isos.html)).
|
||||
💡Confused? Full installation walkthrough is available [here](https://docs.openvk.uk/openvk_engine/centos8_installation/) (CentOS 8 [and](https://almalinux.org/) [family](https://yum.oracle.com/oracle-linux-isos.html)).
|
||||
|
||||
### Looking for Docker or Kubernetes deployment?
|
||||
See `install/automated/docker/README.md` and `install/automated/kubernetes/README.md` for Docker and Kubernetes deployment instructions.
|
||||
|
||||
### If my website uses OpenVK, should I release it's sources?
|
||||
|
||||
It depends. You can keep the sources to yourself if you do not plan to distribute your website binaries. If your website software must be distributed, it can stay non-OSS provided the OpenVK is not used as a primary application and is not modified. If you modified OpenVK for your needs or your work is based on it and you're planning to redistribute this, then you should license it under terms of any LGPL-compatible license (like OSL, GPL, LGPL etc).
|
||||
It depends. You can keep the sources to yourself if you do not plan to distribute your website binaries. If your website software must be distributed, it can stay non-OSS provided the OpenVK is not used as a primary application and is not modified. If you modified OpenVK for your needs or your work is based on it and you are planning to redistribute this, then you should license it under terms of any LGPL-compatible license (like OSL, GPL, LGPL etc).
|
||||
|
||||
## Where can I get assistance?
|
||||
|
||||
You may reach out to us via:
|
||||
|
||||
* [Bug-tracker](https://github.com/openvk/openvk/projects/1)
|
||||
* [Ticketing system](https://openvk.su/support?act=new)
|
||||
* Telegram chat: Go to [our channel](https://t.me/openvkenglish) and open discussion in our channel menu.
|
||||
* [Bug Tracker](https://github.com/openvk/openvk/projects/1)
|
||||
* [Ticketing System](https://openvk.su/support?act=new)
|
||||
* Telegram Chat: Go to [our channel](https://t.me/openvkenglish) and open discussion in our channel menu.
|
||||
* [Reddit](https://www.reddit.com/r/openvk/)
|
||||
* [Discussions](https://github.com/openvk/openvk/discussions)
|
||||
* Matrix chat: #openvk:matrix.org
|
||||
* [GitHub Discussions](https://github.com/openvk/openvk/discussions)
|
||||
* Matrix Chat: #openvk:matrix.org
|
||||
|
||||
**Attention**: bug tracker, board, telegram and matrix chat are public places. And ticketing system is being served by volunteers. If you need to report something, that shouldn't be immediately disclosed to general public (for instance, vulnerability report), please use contact us directly at this email: **openvk [at] tutanota [dot] com**
|
||||
**Attention**: bug tracker, board, Telegram and Matrix chat are public places, ticketing system is being served by volunteers. If you need to report something that should not be immediately disclosed to general public (for instance, a vulnerability), please contact us directly via this email: **openvk [at] tutanota [dot] com**
|
||||
|
||||
<a href="https://codeberg.org/OpenVK/openvk">
|
||||
<img alt="Get it on Codeberg" src="https://codeberg.org/Codeberg/GetItOnCodeberg/media/branch/main/get-it-on-blue-on-white.png" height="60">
|
||||
|
|
22
README_RU.md
22
README_RU.md
|
@ -2,11 +2,11 @@
|
|||
|
||||
_[English](README.md)_
|
||||
|
||||
**OpenVK** - это попытка создать простую CMS, которая ~~косплеит~~ имитирует старый ВКонтакте. На данный момент представленный здесь исходный код проекта пока не является стабильным.
|
||||
**OpenVK** — это попытка создать простую CMS, которая ~~косплеит~~ имитирует старый ВКонтакте. На данный момент, представленный здесь исходный код проекта пока не является стабильным.
|
||||
|
||||
ВКонтакте принадлежит Павлу Дурову и VK Group.
|
||||
|
||||
Честно говоря, мы даже не знаем, работает ли она вообще. Однако, эта версия поддерживается, и мы будем рады принять ваши сообщения об ошибках [в нашем баг-трекере](https://github.com/openvk/openvk/projects/1). Вы также можете отправлять их через [вкладку "Помощь"](https://openvk.su/support?act=new) (для этого вам понадобится учетная запись OVK).
|
||||
Честно говоря, мы даже не знаем, работает ли она вообще. Однако, эта версия поддерживается, и мы будем рады принять ваши сообщения об ошибках [в нашем баг-трекере](https://github.com/openvk/openvk/projects/1). Вы также можете отправлять их через [вкладку "Помощь"](https://openvk.su/support?act=new) (для этого вам понадобится учетная запись OpenVK).
|
||||
|
||||
## Когда выйдет релизная версия?
|
||||
|
||||
|
@ -16,19 +16,15 @@ _[English](README.md)_
|
|||
|
||||
## Инстанции
|
||||
|
||||
* **[openvk.su](https://openvk.su/)**
|
||||
* **[openvk.uk](https://openvk.uk)** - официальное зеркало openvk.su (<https://t.me/openvk/1609>)
|
||||
* **[openvk.co](http://openvk.co)** - ещё одно официальное зеркало openvk.su без TLS (<https://t.me/openvk/1654>)
|
||||
* [social.fetbuk.ru](http://social.fetbuk.ru/)
|
||||
* [vepurovk.xyz](http://vepurovk.xyz/)
|
||||
Список инстанций находится в [нашей вики этого репозитория](https://github.com/openvk/openvk/wiki/Instances-(RU)).
|
||||
|
||||
## Могу ли я создать свою собственную инстанцию OpenVK?
|
||||
|
||||
Да! И всегда пожалуйста.
|
||||
|
||||
Однако, OVK использует Chandler Application Server. Это программное обеспечение требует расширений, которые могут быть не предоставлены вашим хостинг-провайдером (а именно, sodium и yaml. эти расширения доступны на большинстве хостингов ISPManager).
|
||||
Однако, OpenVK использует Chandler Application Server. Это программное обеспечение требует расширений, которые могут быть не предоставлены вашим хостинг-провайдером (а именно, sodium и yaml. Эти расширения доступны на большинстве хостингов ISPManager).
|
||||
|
||||
Если вы хотите, вы можете добавить вашу инстанцию в список выше, чтобы люди могли зарегистрироваться там.
|
||||
Если хотите, вы можете добавить вашу инстанцию в список выше, чтобы люди могли зарегистрироваться там.
|
||||
|
||||
### Процедура установки
|
||||
|
||||
|
@ -38,7 +34,7 @@ _[English](README.md)_
|
|||
|
||||
2. Установите MySQL-совместимую базу данных.
|
||||
|
||||
* Мы рекомендуем использовать Persona Server, но любая MySQL-совместимая база данных должна работать
|
||||
* Мы рекомендуем использовать Persona Server, но любая MySQL-совместимая база данных должна работать.
|
||||
* Сервер должен поддерживать хотя бы MySQL 5.6, рекомендуется использовать MySQL 8.0+.
|
||||
* Поддержка для MySQL 4.1+ находится в процессе, а пока замените `utf8mb4` и `utf8mb4_unicode_520_ci` на `utf8` и `utf8_unicode_ci` в SQL-файлах, соответственно.
|
||||
|
||||
|
@ -70,7 +66,7 @@ ln -s /path/to/chandler/extensions/available/openvk /path/to/chandler/extensions
|
|||
* **Пароль**: `admin`
|
||||
* Перед использованием встроенной учетной записи рекомендуется сменить пароль или отключить её.
|
||||
|
||||
💡Запутались? Полное руководство по установке доступно [здесь](https://docs.openvk.su/openvk_engine/centos8_installation/) (CentOS 8 [и](https://almalinux.org/ru/) [семейство](https://yum.oracle.com/oracle-linux-isos.html)).
|
||||
💡Запутались? Полное руководство по установке доступно [здесь](https://docs.openvk.uk/openvk_engine/centos8_installation/) (CentOS 8 [и](https://almalinux.org/ru/) [семейство](https://yum.oracle.com/oracle-linux-isos.html)).
|
||||
|
||||
# Установка в Docker/Kubernetes
|
||||
Подробные иструкции можно найти в `install/automated/docker/README.md` и `install/automated/kubernetes/README.md` соответственно.
|
||||
|
@ -87,10 +83,10 @@ ln -s /path/to/chandler/extensions/available/openvk /path/to/chandler/extensions
|
|||
* [Помощь в OVK](https://openvk.su/support?act=new)
|
||||
* Telegram-чат: Перейдите на [наш канал](https://t.me/openvk) и откройте обсуждение в меню нашего канала.
|
||||
* [Reddit](https://www.reddit.com/r/openvk/)
|
||||
* [Обсуждения](https://github.com/openvk/openvk/discussions)
|
||||
* [GitHub Discussions](https://github.com/openvk/openvk/discussions)
|
||||
* Чат в Matrix: #ovk:matrix.org
|
||||
|
||||
**Внимание**: баг-трекер, форум, телеграм- и matrix-чат являются публичными местами, и жалобы в OVK обслуживается волонтерами. Если вам нужно сообщить о чем-то, что не должно быть раскрыто широкой публике (например, сообщение об уязвимости), пожалуйста, свяжитесь с нами напрямую по этому адресу: **openvk [собака] tutanota [точка] com**.
|
||||
**Внимание**: баг-трекер, форум, Telegram- и Matrix-чат являются публичными местами, и жалобы в OVK обслуживается волонтерами. Если вам нужно сообщить о чем-то, что не должно быть раскрыто широкой публике (например, сообщение об уязвимости), пожалуйста, свяжитесь с нами напрямую по этому адресу: **openvk [собачка] tutanota [точка] com**.
|
||||
|
||||
<a href="https://codeberg.org/OpenVK/openvk">
|
||||
<img alt="Get it on Codeberg" src="https://codeberg.org/Codeberg/GetItOnCodeberg/media/branch/main/get-it-on-blue-on-white.png" height="60">
|
||||
|
|
39
ServiceAPI/Groups.php
Normal file
39
ServiceAPI/Groups.php
Normal file
|
@ -0,0 +1,39 @@
|
|||
<?php declare(strict_types=1);
|
||||
namespace openvk\ServiceAPI;
|
||||
use openvk\Web\Models\Entities\User;
|
||||
use openvk\Web\Models\Repositories\Clubs;
|
||||
|
||||
class Groups implements Handler
|
||||
{
|
||||
protected $user;
|
||||
protected $groups;
|
||||
|
||||
function __construct(?User $user)
|
||||
{
|
||||
$this->user = $user;
|
||||
$this->groups = new Clubs;
|
||||
}
|
||||
|
||||
function getWriteableClubs(callable $resolve, callable $reject)
|
||||
{
|
||||
$clubs = [];
|
||||
$wclubs = $this->groups->getWriteableClubs($this->user->getId());
|
||||
$count = $this->groups->getWriteableClubsCount($this->user->getId());
|
||||
|
||||
if(!$count) {
|
||||
$reject("You don't have any groups with write access");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
foreach($wclubs as $club) {
|
||||
$clubs[] = [
|
||||
"name" => $club->getName(),
|
||||
"id" => $club->getId(),
|
||||
"avatar" => $club->getAvatarUrl() # если в овк когда-нибудь появится крутой список с аватарками, то можно использовать это поле
|
||||
];
|
||||
}
|
||||
|
||||
$resolve($clubs);
|
||||
}
|
||||
}
|
41
ServiceAPI/Notes.php
Normal file
41
ServiceAPI/Notes.php
Normal file
|
@ -0,0 +1,41 @@
|
|||
<?php
|
||||
|
||||
namespace openvk\ServiceAPI;
|
||||
use openvk\Web\Models\Entities\User;
|
||||
use openvk\Web\Models\Repositories\Notes as NoteRepo;
|
||||
|
||||
class Notes implements Handler
|
||||
{
|
||||
protected $user;
|
||||
protected $notes;
|
||||
|
||||
function __construct(?User $user)
|
||||
{
|
||||
$this->user = $user;
|
||||
$this->notes = new NoteRepo;
|
||||
}
|
||||
|
||||
function getNote(int $noteId, callable $resolve, callable $reject): void
|
||||
{
|
||||
$note = $this->notes->get($noteId);
|
||||
if(!$note || $note->isDeleted())
|
||||
$reject(83, "Note is gone");
|
||||
|
||||
$noteOwner = $note->getOwner();
|
||||
assert($noteOwner instanceof User);
|
||||
if(!$noteOwner->getPrivacyPermission("notes.read", $this->user))
|
||||
$reject(160, "You don't have permission to access this note");
|
||||
|
||||
$resolve([
|
||||
"title" => $note->getName(),
|
||||
"link" => "/note" . $note->getPrettyId(),
|
||||
"html" => $note->getText(),
|
||||
"created" => (string) $note->getPublicationTime(),
|
||||
"author" => [
|
||||
"name" => $noteOwner->getCanonicalName(),
|
||||
"ava" => $noteOwner->getAvatarUrl(),
|
||||
"link" => $noteOwner->getURL(),
|
||||
],
|
||||
]);
|
||||
}
|
||||
}
|
92
ServiceAPI/Photos.php
Normal file
92
ServiceAPI/Photos.php
Normal file
|
@ -0,0 +1,92 @@
|
|||
<?php declare(strict_types=1);
|
||||
namespace openvk\ServiceAPI;
|
||||
use openvk\Web\Models\Entities\User;
|
||||
use openvk\Web\Models\Repositories\{Photos as PhotosRepo, Albums, Clubs};
|
||||
|
||||
class Photos implements Handler
|
||||
{
|
||||
protected $user;
|
||||
protected $photos;
|
||||
|
||||
function __construct(?User $user)
|
||||
{
|
||||
$this->user = $user;
|
||||
$this->photos = new PhotosRepo;
|
||||
}
|
||||
|
||||
function getPhotos(int $page = 1, int $album = 0, callable $resolve, callable $reject)
|
||||
{
|
||||
if($album == 0) {
|
||||
$photos = $this->photos->getEveryUserPhoto($this->user, $page, 24);
|
||||
$count = $this->photos->getUserPhotosCount($this->user);
|
||||
} else {
|
||||
$album = (new Albums)->get($album);
|
||||
|
||||
if(!$album || $album->isDeleted())
|
||||
$reject(55, "Invalid .");
|
||||
|
||||
if($album->getOwner() instanceof User) {
|
||||
if($album->getOwner()->getId() != $this->user->getId())
|
||||
$reject(555, "Access to album denied");
|
||||
} else {
|
||||
if(!$album->getOwner()->canBeModifiedBy($this->user))
|
||||
$reject(555, "Access to album denied");
|
||||
}
|
||||
|
||||
$photos = $album->getPhotos($page, 24);
|
||||
$count = $album->size();
|
||||
}
|
||||
|
||||
$arr = [
|
||||
"count" => $count,
|
||||
"items" => [],
|
||||
];
|
||||
|
||||
foreach($photos as $photo) {
|
||||
$res = json_decode(json_encode($photo->toVkApiStruct()), true);
|
||||
|
||||
$arr["items"][] = $res;
|
||||
}
|
||||
|
||||
$resolve($arr);
|
||||
}
|
||||
|
||||
function getAlbums(int $club, callable $resolve, callable $reject)
|
||||
{
|
||||
$albumsRepo = (new Albums);
|
||||
|
||||
$count = $albumsRepo->getUserAlbumsCount($this->user);
|
||||
$albums = $albumsRepo->getUserAlbums($this->user, 1, $count);
|
||||
|
||||
$arr = [
|
||||
"count" => $count,
|
||||
"items" => [],
|
||||
];
|
||||
|
||||
foreach($albums as $album) {
|
||||
$res = ["id" => $album->getId(), "name" => $album->getName()];
|
||||
|
||||
$arr["items"][] = $res;
|
||||
}
|
||||
|
||||
if($club > 0) {
|
||||
$cluber = (new Clubs)->get($club);
|
||||
|
||||
if(!$cluber || !$cluber->canBeModifiedBy($this->user))
|
||||
$reject(1337, "Invalid (club), or you can't modify him");
|
||||
|
||||
$clubCount = (new Albums)->getClubAlbumsCount($cluber);
|
||||
$clubAlbums = (new Albums)->getClubAlbums($cluber, 1, $clubCount);
|
||||
|
||||
foreach($clubAlbums as $albumr) {
|
||||
$res = ["id" => $albumr->getId(), "name" => $albumr->getName()];
|
||||
|
||||
$arr["items"][] = $res;
|
||||
}
|
||||
|
||||
$arr["count"] = $arr["count"] + $clubCount;
|
||||
}
|
||||
|
||||
$resolve($arr);
|
||||
}
|
||||
}
|
76
ServiceAPI/Search.php
Normal file
76
ServiceAPI/Search.php
Normal file
|
@ -0,0 +1,76 @@
|
|||
<?php declare(strict_types=1);
|
||||
namespace openvk\ServiceAPI;
|
||||
use openvk\Web\Models\Entities\{User, Club};
|
||||
use openvk\Web\Models\Repositories\{Users, Clubs, Videos};
|
||||
use Chandler\Database\DatabaseConnection;
|
||||
|
||||
class Search implements Handler
|
||||
{
|
||||
protected $user;
|
||||
private $users;
|
||||
private $clubs;
|
||||
private $videos;
|
||||
|
||||
function __construct(?User $user)
|
||||
{
|
||||
$this->user = $user;
|
||||
$this->users = new Users;
|
||||
$this->clubs = new Clubs;
|
||||
$this->videos = new Videos;
|
||||
}
|
||||
|
||||
function fastSearch(string $query, string $type = "users", callable $resolve, callable $reject)
|
||||
{
|
||||
if($query == "" || strlen($query) < 3)
|
||||
$reject(12, "No input or input < 3");
|
||||
|
||||
$repo;
|
||||
$sort;
|
||||
|
||||
switch($type) {
|
||||
default:
|
||||
case "users":
|
||||
$repo = (new Users);
|
||||
$sort = "rating DESC";
|
||||
|
||||
break;
|
||||
case "groups":
|
||||
$repo = (new Clubs);
|
||||
$sort = "id ASC";
|
||||
|
||||
break;
|
||||
case "videos":
|
||||
$repo = (new Videos);
|
||||
$sort = "created ASC";
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
$res = $repo->find($query, ["doNotSearchMe" => $this->user->getId()], $sort);
|
||||
|
||||
$results = array_slice(iterator_to_array($res), 0, 5);
|
||||
|
||||
$count = sizeof($results);
|
||||
|
||||
$arr = [
|
||||
"count" => $count,
|
||||
"items" => []
|
||||
];
|
||||
|
||||
if(sizeof($results) < 1) {
|
||||
$reject(2, "No results");
|
||||
}
|
||||
|
||||
foreach($results as $res) {
|
||||
$arr["items"][] = [
|
||||
"id" => $res->getId(),
|
||||
"name" => $type == "users" ? $res->getCanonicalName() : $res->getName(),
|
||||
"avatar" => $type != "videos" ? $res->getAvatarUrl() : $res->getThumbnailURL(),
|
||||
"url" => $type != "videos" ? $res->getUrl() : "/video".$res->getPrettyId(),
|
||||
"description" => ovk_proc_strtr($res->getDescription() ?? "...", 40)
|
||||
];
|
||||
}
|
||||
|
||||
$resolve($arr);
|
||||
}
|
||||
}
|
|
@ -2,17 +2,20 @@
|
|||
namespace openvk\ServiceAPI;
|
||||
use openvk\Web\Models\Entities\Post;
|
||||
use openvk\Web\Models\Entities\User;
|
||||
use openvk\Web\Models\Repositories\Posts;
|
||||
use openvk\Web\Models\Repositories\{Posts, Notes, Videos};
|
||||
|
||||
class Wall implements Handler
|
||||
{
|
||||
protected $user;
|
||||
protected $posts;
|
||||
protected $notes;
|
||||
|
||||
function __construct(?User $user)
|
||||
{
|
||||
$this->user = $user;
|
||||
$this->posts = new Posts;
|
||||
$this->notes = new Notes;
|
||||
$this->videos = new Videos;
|
||||
}
|
||||
|
||||
function getPost(int $id, callable $resolve, callable $reject): void
|
||||
|
@ -71,4 +74,67 @@ class Wall implements Handler
|
|||
|
||||
$resolve($post->getId());
|
||||
}
|
||||
|
||||
function getMyNotes(callable $resolve, callable $reject)
|
||||
{
|
||||
$count = $this->notes->getUserNotesCount($this->user);
|
||||
$myNotes = $this->notes->getUserNotes($this->user, 1, $count);
|
||||
|
||||
$arr = [
|
||||
"count" => $count,
|
||||
"closed" => $this->user->getPrivacySetting("notes.read"),
|
||||
"items" => [],
|
||||
];
|
||||
|
||||
foreach($myNotes as $note) {
|
||||
$arr["items"][] = [
|
||||
"id" => $note->getId(),
|
||||
"name" => ovk_proc_strtr($note->getName(), 30),
|
||||
#"preview" => $note->getPreview()
|
||||
];
|
||||
}
|
||||
|
||||
$resolve($arr);
|
||||
}
|
||||
|
||||
function getVideos(int $page = 1, callable $resolve, callable $reject)
|
||||
{
|
||||
$videos = $this->videos->getByUser($this->user, $page, 8);
|
||||
$count = $this->videos->getUserVideosCount($this->user);
|
||||
|
||||
$arr = [
|
||||
"count" => $count,
|
||||
"items" => [],
|
||||
];
|
||||
|
||||
foreach($videos as $video) {
|
||||
$res = json_decode(json_encode($video->toVkApiStruct()), true);
|
||||
$res["video"]["author_name"] = $video->getOwner()->getCanonicalName();
|
||||
|
||||
$arr["items"][] = $res;
|
||||
}
|
||||
|
||||
$resolve($arr);
|
||||
}
|
||||
|
||||
function searchVideos(int $page = 1, string $query, callable $resolve, callable $reject)
|
||||
{
|
||||
$dbc = $this->videos->find($query);
|
||||
$videos = $dbc->page($page, 8);
|
||||
$count = $dbc->size();
|
||||
|
||||
$arr = [
|
||||
"count" => $count,
|
||||
"items" => [],
|
||||
];
|
||||
|
||||
foreach($videos as $video) {
|
||||
$res = json_decode(json_encode($video->toVkApiStruct()), true);
|
||||
$res["video"]["author_name"] = $video->getOwner()->getCanonicalName();
|
||||
|
||||
$arr["items"][] = $res;
|
||||
}
|
||||
|
||||
$resolve($arr);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -45,13 +45,12 @@ final class Account extends VKAPIRequestHandler
|
|||
{
|
||||
$this->requireUser();
|
||||
|
||||
$this->getUser()->setOnline(time());
|
||||
$this->getUser()->save();
|
||||
$this->getUser()->updOnline($this->getPlatform());
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
function setOffline(): object
|
||||
function setOffline(): int
|
||||
{
|
||||
$this->requireUser();
|
||||
|
||||
|
@ -67,6 +66,8 @@ final class Account extends VKAPIRequestHandler
|
|||
|
||||
function getCounters(string $filter = ""): object
|
||||
{
|
||||
$this->requireUser();
|
||||
|
||||
return (object) [
|
||||
"friends" => $this->getUser()->getFollowersCount(),
|
||||
"notifications" => $this->getUser()->getNotificationsCount(),
|
||||
|
@ -79,6 +80,8 @@ final class Account extends VKAPIRequestHandler
|
|||
function saveProfileInfo(string $first_name = "", string $last_name = "", string $screen_name = "", int $sex = -1, int $relation = -1, string $bdate = "", int $bdate_visibility = -1, string $home_town = "", string $status = ""): object
|
||||
{
|
||||
$this->requireUser();
|
||||
$this->willExecuteWriteAction();
|
||||
|
||||
$user = $this->getUser();
|
||||
|
||||
$output = [
|
||||
|
|
431
VKAPI/Handlers/Board.php
Normal file
431
VKAPI/Handlers/Board.php
Normal file
|
@ -0,0 +1,431 @@
|
|||
<?php declare(strict_types=1);
|
||||
namespace openvk\VKAPI\Handlers;
|
||||
use openvk\VKAPI\Handlers\Wall;
|
||||
use openvk\Web\Models\Repositories\Topics as TopicsRepo;
|
||||
use openvk\Web\Models\Repositories\Clubs as ClubsRepo;
|
||||
use openvk\Web\Models\Repositories\Photos as PhotosRepo;
|
||||
use openvk\Web\Models\Repositories\Videos as VideosRepo;
|
||||
use openvk\Web\Models\Repositories\Comments as CommentsRepo;
|
||||
use openvk\Web\Models\Entities\{Topic, Comment, User, Photo, Video};
|
||||
|
||||
final class Board extends VKAPIRequestHandler
|
||||
{
|
||||
# 13/13
|
||||
function addTopic(int $group_id, string $title, string $text = "", bool $from_group = true, string $attachments = "")
|
||||
{
|
||||
$this->requireUser();
|
||||
$this->willExecuteWriteAction();
|
||||
|
||||
$club = (new ClubsRepo)->get($group_id);
|
||||
|
||||
if(!$club) {
|
||||
$this->fail(403, "Invalid club");
|
||||
}
|
||||
|
||||
if(!$club->canBeModifiedBy($this->getUser()) && !$club->isEveryoneCanCreateTopics()) {
|
||||
$this->fail(403, "Access to club denied");
|
||||
}
|
||||
|
||||
$flags = 0;
|
||||
|
||||
if($from_group == true && $club->canBeModifiedBy($this->getUser()))
|
||||
$flags |= 0b10000000;
|
||||
|
||||
$topic = new Topic;
|
||||
$topic->setGroup($club->getId());
|
||||
$topic->setOwner($this->getUser()->getId());
|
||||
$topic->setTitle(ovk_proc_strtr($title, 127));
|
||||
$topic->setCreated(time());
|
||||
$topic->setFlags($flags);
|
||||
$topic->save();
|
||||
|
||||
if(!empty($text)) {
|
||||
$comment = new Comment;
|
||||
$comment->setOwner($this->getUser()->getId());
|
||||
$comment->setModel(get_class($topic));
|
||||
$comment->setTarget($topic->getId());
|
||||
$comment->setContent($text);
|
||||
$comment->setCreated(time());
|
||||
$comment->setFlags($flags);
|
||||
$comment->save();
|
||||
|
||||
if(!empty($attachments)) {
|
||||
$attachmentsArr = explode(",", $attachments);
|
||||
# блин а мне это везде копировать типа
|
||||
|
||||
if(sizeof($attachmentsArr) > 10)
|
||||
$this->fail(50, "Error: too many attachments");
|
||||
|
||||
foreach($attachmentsArr as $attac) {
|
||||
$attachmentType = NULL;
|
||||
|
||||
if(str_contains($attac, "photo"))
|
||||
$attachmentType = "photo";
|
||||
elseif(str_contains($attac, "video"))
|
||||
$attachmentType = "video";
|
||||
else
|
||||
$this->fail(205, "Unknown attachment type");
|
||||
|
||||
$attachment = str_replace($attachmentType, "", $attac);
|
||||
|
||||
$attachmentOwner = (int)explode("_", $attachment)[0];
|
||||
$attachmentId = (int)end(explode("_", $attachment));
|
||||
|
||||
$attacc = NULL;
|
||||
|
||||
if($attachmentType == "photo") {
|
||||
$attacc = (new PhotosRepo)->getByOwnerAndVID($attachmentOwner, $attachmentId);
|
||||
if(!$attacc || $attacc->isDeleted())
|
||||
$this->fail(100, "Photo does not exists");
|
||||
if($attacc->getOwner()->getId() != $this->getUser()->getId())
|
||||
$this->fail(43, "You do not have access to this photo");
|
||||
|
||||
$comment->attach($attacc);
|
||||
} elseif($attachmentType == "video") {
|
||||
$attacc = (new VideosRepo)->getByOwnerAndVID($attachmentOwner, $attachmentId);
|
||||
if(!$attacc || $attacc->isDeleted())
|
||||
$this->fail(100, "Video does not exists");
|
||||
if($attacc->getOwner()->getId() != $this->getUser()->getId())
|
||||
$this->fail(43, "You do not have access to this video");
|
||||
|
||||
$comment->attach($attacc);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return $topic->getId();
|
||||
}
|
||||
|
||||
function closeTopic(int $group_id, int $topic_id)
|
||||
{
|
||||
$this->requireUser();
|
||||
$this->willExecuteWriteAction();
|
||||
|
||||
$topic = (new TopicsRepo)->getTopicById($group_id, $topic_id);
|
||||
|
||||
if(!$topic || !$topic->getClub() || !$topic->getClub()->canBeModifiedBy($this->getUser())) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(!$topic->isClosed()) {
|
||||
$topic->setClosed(1);
|
||||
$topic->save();
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
function createComment(int $group_id, int $topic_id, string $message = "", string $attachments = "", bool $from_group = true)
|
||||
{
|
||||
$this->requireUser();
|
||||
$this->willExecuteWriteAction();
|
||||
|
||||
if(empty($message) && empty($attachments)) {
|
||||
$this->fail(100, "Required parameter 'message' missing.");
|
||||
}
|
||||
|
||||
$topic = (new TopicsRepo)->getTopicById($group_id, $topic_id);
|
||||
|
||||
if(!$topic || $topic->isDeleted() || $topic->isClosed()) {
|
||||
$this->fail(100, "Topic is deleted, closed or invalid.");
|
||||
}
|
||||
|
||||
$flags = 0;
|
||||
|
||||
if($from_group != 0 && !is_null($topic->getClub()) && $topic->getClub()->canBeModifiedBy($this->user))
|
||||
$flags |= 0b10000000;
|
||||
|
||||
if(strlen($message) > 300) {
|
||||
$this->fail(20, "Comment is too long.");
|
||||
}
|
||||
|
||||
$comment = new Comment;
|
||||
$comment->setOwner($this->getUser()->getId());
|
||||
$comment->setModel(get_class($topic));
|
||||
$comment->setTarget($topic->getId());
|
||||
$comment->setContent($message);
|
||||
$comment->setCreated(time());
|
||||
$comment->setFlags($flags);
|
||||
$comment->save();
|
||||
|
||||
if(!empty($attachments)) {
|
||||
$attachmentsArr = explode(",", $attachments);
|
||||
|
||||
if(sizeof($attachmentsArr) > 10)
|
||||
$this->fail(50, "Error: too many attachments");
|
||||
|
||||
foreach($attachmentsArr as $attac) {
|
||||
$attachmentType = NULL;
|
||||
|
||||
if(str_contains($attac, "photo"))
|
||||
$attachmentType = "photo";
|
||||
elseif(str_contains($attac, "video"))
|
||||
$attachmentType = "video";
|
||||
else
|
||||
$this->fail(205, "Unknown attachment type");
|
||||
|
||||
$attachment = str_replace($attachmentType, "", $attac);
|
||||
|
||||
$attachmentOwner = (int)explode("_", $attachment)[0];
|
||||
$attachmentId = (int)end(explode("_", $attachment));
|
||||
|
||||
$attacc = NULL;
|
||||
|
||||
if($attachmentType == "photo") {
|
||||
$attacc = (new PhotosRepo)->getByOwnerAndVID($attachmentOwner, $attachmentId);
|
||||
if(!$attacc || $attacc->isDeleted())
|
||||
$this->fail(100, "Photo does not exists");
|
||||
if($attacc->getOwner()->getId() != $this->getUser()->getId())
|
||||
$this->fail(43, "You do not have access to this photo");
|
||||
|
||||
$comment->attach($attacc);
|
||||
} elseif($attachmentType == "video") {
|
||||
$attacc = (new VideosRepo)->getByOwnerAndVID($attachmentOwner, $attachmentId);
|
||||
if(!$attacc || $attacc->isDeleted())
|
||||
$this->fail(100, "Video does not exists");
|
||||
if($attacc->getOwner()->getId() != $this->getUser()->getId())
|
||||
$this->fail(43, "You do not have access to this video");
|
||||
|
||||
$comment->attach($attacc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $comment->getId();
|
||||
}
|
||||
|
||||
function deleteComment(int $comment_id, int $group_id = 0, int $topic_id = 0)
|
||||
{
|
||||
$this->requireUser();
|
||||
$this->willExecuteWriteAction();
|
||||
|
||||
$comment = (new CommentsRepo)->get($comment_id);
|
||||
|
||||
if($comment->isDeleted() || !$comment || !$comment->canBeDeletedBy($this->getUser()))
|
||||
$this->fail(403, "Access to comment denied");
|
||||
|
||||
$comment->delete();
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
function deleteTopic(int $group_id, int $topic_id)
|
||||
{
|
||||
$this->requireUser();
|
||||
$this->willExecuteWriteAction();
|
||||
|
||||
$topic = (new TopicsRepo)->getTopicById($group_id, $topic_id);
|
||||
|
||||
if(!$topic || !$topic->getClub() || $topic->isDeleted() || !$topic->getClub()->canBeModifiedBy($this->getUser())) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$topic->deleteTopic();
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
function editComment(int $comment_id, int $group_id = 0, int $topic_id = 0, string $message, string $attachments)
|
||||
{
|
||||
/*
|
||||
$this->requireUser();
|
||||
$this->willExecuteWriteAction();
|
||||
|
||||
$comment = (new CommentsRepo)->get($comment_id);
|
||||
|
||||
if($comment->getOwner() != $this->getUser()->getId())
|
||||
$this->fail(15, "Access to comment denied");
|
||||
|
||||
$comment->setContent($message);
|
||||
$comment->setEdited(time());
|
||||
$comment->save();
|
||||
*/
|
||||
return 1;
|
||||
}
|
||||
|
||||
function editTopic(int $group_id, int $topic_id, string $title)
|
||||
{
|
||||
$this->requireUser();
|
||||
$this->willExecuteWriteAction();
|
||||
|
||||
$topic = (new TopicsRepo)->getTopicById($group_id, $topic_id);
|
||||
|
||||
if(!$topic || !$topic->getClub() || $topic->isDeleted() || !$topic->getClub()->canBeModifiedBy($this->getUser())) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$topic->setTitle(ovk_proc_strtr($title, 127));
|
||||
|
||||
$topic->save();
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
function fixTopic(int $group_id, int $topic_id)
|
||||
{
|
||||
$this->requireUser();
|
||||
$this->willExecuteWriteAction();
|
||||
|
||||
$topic = (new TopicsRepo)->getTopicById($group_id, $topic_id);
|
||||
|
||||
if(!$topic || !$topic->getClub() || !$topic->getClub()->canBeModifiedBy($this->getUser())) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$topic->setPinned(1);
|
||||
|
||||
$topic->save();
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
function getComments(int $group_id, int $topic_id, bool $need_likes = false, int $start_comment_id = 0, int $offset = 0, int $count = 40, bool $extended = false, string $sort = "asc")
|
||||
{
|
||||
# start_comment_id ne robit
|
||||
$this->requireUser();
|
||||
$this->willExecuteWriteAction();
|
||||
|
||||
$topic = (new TopicsRepo)->getTopicById($group_id, $topic_id);
|
||||
|
||||
if(!$topic || !$topic->getClub() || $topic->isDeleted()) {
|
||||
$this->fail(5, "Invalid topic");
|
||||
}
|
||||
|
||||
$arr = [
|
||||
"items" => []
|
||||
];
|
||||
|
||||
$comms = array_slice(iterator_to_array($topic->getComments(1, $count + $offset)), $offset);
|
||||
foreach($comms as $comm) {
|
||||
$arr["items"][] = $this->getApiBoardComment($comm, $need_likes);
|
||||
|
||||
if($extended) {
|
||||
if($comm->getOwner() instanceof \openvk\Web\Models\Entities\User) {
|
||||
$arr["profiles"][] = $comm->getOwner()->toVkApiStruct();
|
||||
}
|
||||
|
||||
if($comm->getOwner() instanceof \openvk\Web\Models\Entities\Club) {
|
||||
$arr["groups"][] = $comm->getOwner()->toVkApiStruct();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $arr;
|
||||
}
|
||||
|
||||
function getTopics(int $group_id, string $topic_ids = "", int $order = 1, int $offset = 0, int $count = 40, bool $extended = false, int $preview = 0, int $preview_length = 90)
|
||||
{
|
||||
# order и extended ничё не делают
|
||||
$this->requireUser();
|
||||
$this->willExecuteWriteAction();
|
||||
|
||||
$arr = [];
|
||||
$club = (new ClubsRepo)->get($group_id);
|
||||
|
||||
$topics = array_slice(iterator_to_array((new TopicsRepo)->getClubTopics($club, 1, $count + $offset)), $offset);
|
||||
$arr["count"] = (new TopicsRepo)->getClubTopicsCount($club);
|
||||
$arr["items"] = [];
|
||||
$arr["default_order"] = $order;
|
||||
$arr["can_add_topics"] = $club->canBeModifiedBy($this->getUser()) ? true : $club->isEveryoneCanCreateTopics() ? true : false;
|
||||
$arr["profiles"] = [];
|
||||
|
||||
if(empty($topic_ids)) {
|
||||
foreach($topics as $topic) {
|
||||
if($topic->isDeleted()) continue;
|
||||
$arr["items"][] = $topic->toVkApiStruct($preview, $preview_length > 1 ? $preview_length : 90);
|
||||
}
|
||||
} else {
|
||||
$topics = explode(',', $topic_ids);
|
||||
|
||||
foreach($topics as $topic) {
|
||||
$id = explode("_", $topic);
|
||||
$topicy = (new TopicsRepo)->getTopicById((int)$id[0], (int)$id[1]);
|
||||
|
||||
if($topicy && !$topicy->isDeleted()) {
|
||||
$arr["items"][] = $topicy->toVkApiStruct($preview, $preview_length > 1 ? $preview_length : 90);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $arr;
|
||||
}
|
||||
|
||||
function openTopic(int $group_id, int $topic_id)
|
||||
{
|
||||
$this->requireUser();
|
||||
$this->willExecuteWriteAction();
|
||||
|
||||
$topic = (new TopicsRepo)->getTopicById($group_id, $topic_id);
|
||||
|
||||
if(!$topic || !$topic->getClub() || !$topic->isDeleted() || !$topic->getClub()->canBeModifiedBy($this->getUser())) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if($topic->isClosed()) {
|
||||
$topic->setClosed(0);
|
||||
$topic->save();
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
function restoreComment(int $group_id, int $topic_id, int $comment_id)
|
||||
{
|
||||
$this->fail(501, "Not implemented");
|
||||
}
|
||||
|
||||
function unfixTopic(int $group_id, int $topic_id)
|
||||
{
|
||||
$this->requireUser();
|
||||
$this->willExecuteWriteAction();
|
||||
|
||||
$topic = (new TopicsRepo)->getTopicById($group_id, $topic_id);
|
||||
|
||||
if(!$topic || !$topic->getClub() || !$topic->getClub()->canBeModifiedBy($this->getUser())) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if($topic->isPinned()) {
|
||||
$topic->setClosed(0);
|
||||
$topic->save();
|
||||
}
|
||||
|
||||
$topic->setPinned(0);
|
||||
|
||||
$topic->save();
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
private function getApiBoardComment(?Comment $comment, bool $need_likes = false)
|
||||
{
|
||||
$res = (object) [];
|
||||
|
||||
$res->id = $comment->getId();
|
||||
$res->from_id = $comment->getOwner()->getId();
|
||||
$res->date = $comment->getPublicationTime()->timestamp();
|
||||
$res->text = $comment->getText(false);
|
||||
$res->attachments = [];
|
||||
$res->likes = [];
|
||||
if($need_likes) {
|
||||
$res->likes = [
|
||||
"count" => $comment->getLikesCount(),
|
||||
"user_likes" => (int) $comment->hasLikeFrom($this->getUser()),
|
||||
"can_like" => 1 # а чё типо не может ахахаххахах
|
||||
];
|
||||
}
|
||||
|
||||
foreach($comment->getChildren() as $attachment) {
|
||||
if($attachment->isDeleted())
|
||||
continue;
|
||||
|
||||
$res->attachments[] = $attachment->toVkApiStruct();
|
||||
}
|
||||
|
||||
return $res;
|
||||
}
|
||||
}
|
|
@ -66,6 +66,7 @@ final class Friends extends VKAPIRequestHandler
|
|||
function add(string $user_id): int
|
||||
{
|
||||
$this->requireUser();
|
||||
$this->willExecuteWriteAction();
|
||||
|
||||
$users = new UsersRepo;
|
||||
$user = $users->get(intval($user_id));
|
||||
|
@ -96,6 +97,7 @@ final class Friends extends VKAPIRequestHandler
|
|||
function delete(string $user_id): int
|
||||
{
|
||||
$this->requireUser();
|
||||
$this->willExecuteWriteAction();
|
||||
|
||||
$users = new UsersRepo;
|
||||
|
||||
|
@ -152,10 +154,7 @@ final class Friends extends VKAPIRequestHandler
|
|||
$response = $followers;
|
||||
$usersApi = new Users($this->getUser());
|
||||
|
||||
if($extended == 1)
|
||||
$response = $usersApi->get(implode(',', $followers), $fields, 0, $count);
|
||||
else
|
||||
$response = $usersApi->get(implode(',', $followers), "", 0, $count);
|
||||
$response = $usersApi->get(implode(',', $followers), $fields, 0, $count);
|
||||
|
||||
foreach($response as $user)
|
||||
$user->user_id = $user->id;
|
||||
|
|
174
VKAPI/Handlers/Gifts.php
Normal file
174
VKAPI/Handlers/Gifts.php
Normal file
|
@ -0,0 +1,174 @@
|
|||
<?php declare(strict_types=1);
|
||||
namespace openvk\VKAPI\Handlers;
|
||||
use openvk\Web\Models\Repositories\Users as UsersRepo;
|
||||
use openvk\Web\Models\Repositories\Gifts as GiftsRepo;
|
||||
use openvk\Web\Models\Entities\Notifications\GiftNotification;
|
||||
|
||||
final class Gifts extends VKAPIRequestHandler
|
||||
{
|
||||
function get(int $user_id, int $count = 10, int $offset = 0)
|
||||
{
|
||||
$this->requireUser();
|
||||
|
||||
$i = 0;
|
||||
|
||||
$i += $offset;
|
||||
|
||||
$user = (new UsersRepo)->get($user_id);
|
||||
|
||||
if(!$user || $user->isDeleted())
|
||||
$this->fail(177, "Invalid user");
|
||||
|
||||
$gift_item = [];
|
||||
|
||||
$userGifts = array_slice(iterator_to_array($user->getGifts(1, $count, false)), $offset);
|
||||
|
||||
if(sizeof($userGifts) < 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
foreach($userGifts as $gift) {
|
||||
if($i < $count) {
|
||||
$gift_item[] = [
|
||||
"id" => $i,
|
||||
"from_id" => $gift->anon == true ? 0 : $gift->sender->getId(),
|
||||
"message" => $gift->caption == NULL ? "" : $gift->caption,
|
||||
"date" => $gift->sent->timestamp(),
|
||||
"gift" => [
|
||||
"id" => $gift->gift->getId(),
|
||||
"thumb_256" => $gift->gift->getImage(2),
|
||||
"thumb_96" => $gift->gift->getImage(2),
|
||||
"thumb_48" => $gift->gift->getImage(2)
|
||||
],
|
||||
"privacy" => 0
|
||||
];
|
||||
}
|
||||
$i+=1;
|
||||
}
|
||||
|
||||
return $gift_item;
|
||||
}
|
||||
|
||||
function send(int $user_ids, int $gift_id, string $message = "", int $privacy = 0)
|
||||
{
|
||||
$this->requireUser();
|
||||
$this->willExecuteWriteAction();
|
||||
|
||||
$user = (new UsersRepo)->get((int) $user_ids);
|
||||
|
||||
if(!OPENVK_ROOT_CONF['openvk']['preferences']['commerce'])
|
||||
$this->fail(105, "Commerce is disabled on this instance");
|
||||
|
||||
if(!$user || $user->isDeleted())
|
||||
$this->fail(177, "Invalid user");
|
||||
|
||||
$gift = (new GiftsRepo)->get($gift_id);
|
||||
|
||||
if(!$gift)
|
||||
$this->fail(165, "Invalid gift");
|
||||
|
||||
$price = $gift->getPrice();
|
||||
$coinsLeft = $this->getUser()->getCoins() - $price;
|
||||
|
||||
if(!$gift->canUse($this->getUser()))
|
||||
return (object)
|
||||
[
|
||||
"success" => 0,
|
||||
"user_ids" => $user_ids,
|
||||
"error" => "You don't have any more of these gifts."
|
||||
];
|
||||
|
||||
if($coinsLeft < 0)
|
||||
return (object)
|
||||
[
|
||||
"success" => 0,
|
||||
"user_ids" => $user_ids,
|
||||
"error" => "You don't have enough voices."
|
||||
];
|
||||
|
||||
$user->gift($this->getUser(), $gift, $message);
|
||||
$gift->used();
|
||||
|
||||
$this->getUser()->setCoins($coinsLeft);
|
||||
$this->getUser()->save();
|
||||
|
||||
$notification = new GiftNotification($user, $this->getUser(), $gift, $message);
|
||||
$notification->emit();
|
||||
|
||||
return (object)
|
||||
[
|
||||
"success" => 1,
|
||||
"user_ids" => $user_ids,
|
||||
"withdraw_votes" => $price
|
||||
];
|
||||
}
|
||||
|
||||
function delete()
|
||||
{
|
||||
$this->requireUser();
|
||||
$this->willExecuteWriteAction();
|
||||
|
||||
$this->fail(501, "Not implemented");
|
||||
}
|
||||
|
||||
# этих методов не было в ВК, но я их добавил чтобы можно было отобразить список подарков
|
||||
function getCategories(bool $extended = false, int $page = 1)
|
||||
{
|
||||
$cats = (new GiftsRepo)->getCategories($page);
|
||||
$categ = [];
|
||||
$i = 0;
|
||||
|
||||
if(!OPENVK_ROOT_CONF['openvk']['preferences']['commerce'])
|
||||
$this->fail(105, "Commerce is disabled on this instance");
|
||||
|
||||
foreach($cats as $cat) {
|
||||
$categ[$i] = [
|
||||
"name" => $cat->getName(),
|
||||
"description" => $cat->getDescription(),
|
||||
"id" => $cat->getId(),
|
||||
"thumbnail" => $cat->getThumbnailURL(),
|
||||
];
|
||||
|
||||
if($extended == true) {
|
||||
$categ[$i]["localizations"] = [];
|
||||
foreach(getLanguages() as $lang) {
|
||||
$code = $lang["code"];
|
||||
$categ[$i]["localizations"][$code] =
|
||||
[
|
||||
"name" => $cat->getName($code),
|
||||
"desc" => $cat->getDescription($code),
|
||||
];
|
||||
}
|
||||
}
|
||||
$i++;
|
||||
}
|
||||
|
||||
return $categ;
|
||||
}
|
||||
|
||||
function getGiftsInCategory(int $id, int $page = 1)
|
||||
{
|
||||
$this->requireUser();
|
||||
|
||||
if(!OPENVK_ROOT_CONF['openvk']['preferences']['commerce'])
|
||||
$this->fail(105, "Commerce is disabled on this instance");
|
||||
|
||||
if(!(new GiftsRepo)->getCat($id))
|
||||
$this->fail(177, "Category not found");
|
||||
|
||||
$giftz = ((new GiftsRepo)->getCat($id))->getGifts($page);
|
||||
$gifts = [];
|
||||
|
||||
foreach($giftz as $gift) {
|
||||
$gifts[] = [
|
||||
"name" => $gift->getName(),
|
||||
"image" => $gift->getImage(2),
|
||||
"usages_left" => (int)$gift->getUsagesLeft($this->getUser()),
|
||||
"price" => $gift->getPrice(), # голосов
|
||||
"is_free" => $gift->isFree()
|
||||
];
|
||||
}
|
||||
|
||||
return $gifts;
|
||||
}
|
||||
}
|
|
@ -2,6 +2,7 @@
|
|||
namespace openvk\VKAPI\Handlers;
|
||||
use openvk\Web\Models\Repositories\Clubs as ClubsRepo;
|
||||
use openvk\Web\Models\Repositories\Users as UsersRepo;
|
||||
use openvk\Web\Models\Entities\Club;
|
||||
|
||||
final class Groups extends VKAPIRequestHandler
|
||||
{
|
||||
|
@ -185,7 +186,7 @@ final class Groups extends VKAPIRequestHandler
|
|||
$response[$i]->site = $clb->getWebsite();
|
||||
break;
|
||||
case "description":
|
||||
$response[$i]->desctiption = $clb->getDescription();
|
||||
$response[$i]->description = $clb->getDescription();
|
||||
break;
|
||||
case "contacts":
|
||||
$contacts;
|
||||
|
@ -237,6 +238,7 @@ final class Groups extends VKAPIRequestHandler
|
|||
function join(int $group_id)
|
||||
{
|
||||
$this->requireUser();
|
||||
$this->willExecuteWriteAction();
|
||||
|
||||
$club = (new ClubsRepo)->get($group_id);
|
||||
|
||||
|
@ -251,6 +253,7 @@ final class Groups extends VKAPIRequestHandler
|
|||
function leave(int $group_id)
|
||||
{
|
||||
$this->requireUser();
|
||||
$this->willExecuteWriteAction();
|
||||
|
||||
$club = (new ClubsRepo)->get($group_id);
|
||||
|
||||
|
@ -261,4 +264,272 @@ final class Groups extends VKAPIRequestHandler
|
|||
|
||||
return 1;
|
||||
}
|
||||
|
||||
function create(string $title, string $description = "", string $type = "group", int $public_category = 1, int $public_subcategory = 1, int $subtype = 1)
|
||||
{
|
||||
$this->requireUser();
|
||||
$this->willExecuteWriteAction();
|
||||
|
||||
$club = new Club;
|
||||
|
||||
$club->setName($title);
|
||||
$club->setAbout($description);
|
||||
$club->setOwner($this->getUser()->getId());
|
||||
$club->save();
|
||||
|
||||
$club->toggleSubscription($this->getUser());
|
||||
|
||||
return $this->getById((string)$club->getId());
|
||||
}
|
||||
|
||||
function edit(
|
||||
int $group_id,
|
||||
string $title = NULL,
|
||||
string $description = NULL,
|
||||
string $screen_name = NULL,
|
||||
string $website = NULL,
|
||||
int $wall = NULL,
|
||||
int $topics = NULL,
|
||||
int $adminlist = NULL,
|
||||
int $topicsAboveWall = NULL,
|
||||
int $hideFromGlobalFeed = NULL)
|
||||
{
|
||||
$this->requireUser();
|
||||
$this->willExecuteWriteAction();
|
||||
|
||||
$club = (new ClubsRepo)->get($group_id);
|
||||
|
||||
if(!$club) $this->fail(203, "Club not found");
|
||||
if(!$club || !$club->canBeModifiedBy($this->getUser())) $this->fail(15, "You can't modify this group.");
|
||||
if(!empty($screen_name) && !$club->setShortcode($screen_name)) $this->fail(103, "Invalid shortcode.");
|
||||
|
||||
!is_null($title) ? $club->setName($title) : NULL;
|
||||
!is_null($description) ? $club->setAbout($description) : NULL;
|
||||
!is_null($screen_name) ? $club->setShortcode($screen_name) : NULL;
|
||||
!is_null($website) ? $club->setWebsite((!parse_url($website, PHP_URL_SCHEME) ? "https://" : "") . $website) : NULL;
|
||||
!is_null($wall) ? $club->setWall($wall) : NULL;
|
||||
!is_null($topics) ? $club->setEveryone_Can_Create_Topics($topics) : NULL;
|
||||
!is_null($adminlist) ? $club->setAdministrators_List_Display($adminlist) : NULL;
|
||||
!is_null($topicsAboveWall) ? $club->setDisplay_Topics_Above_Wall($topicsAboveWall) : NULL;
|
||||
!is_null($hideFromGlobalFeed) ? $club->setHide_From_Global_Feed($hideFromGlobalFeed) : NULL;
|
||||
|
||||
$club->save();
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
function getMembers(string $group_id, string $sort = "id_asc", int $offset = 0, int $count = 100, string $fields = "", string $filter = "any")
|
||||
{
|
||||
# bdate,can_post,can_see_all_posts,can_see_audio,can_write_private_message,city,common_count,connections,contacts,country,domain,education,has_mobile,last_seen,lists,online,online_mobile,photo_100,photo_200,photo_200_orig,photo_400_orig,photo_50,photo_max,photo_max_orig,relation,relatives,schools,sex,site,status,universities
|
||||
$club = (new ClubsRepo)->get((int) $group_id);
|
||||
if(!$club)
|
||||
$this->fail(125, "Invalid group id");
|
||||
|
||||
$sorter = "follower ASC";
|
||||
|
||||
switch($sort) {
|
||||
default:
|
||||
case "time_asc":
|
||||
case "id_asc":
|
||||
$sorter = "follower ASC";
|
||||
break;
|
||||
case "time_desc":
|
||||
case "id_desc":
|
||||
$sorter = "follower DESC";
|
||||
break;
|
||||
}
|
||||
|
||||
$members = array_slice(iterator_to_array($club->getFollowers(1, $count, $sorter)), $offset);
|
||||
$arr = (object) [
|
||||
"count" => count($members),
|
||||
"items" => array()];
|
||||
|
||||
$filds = explode(",", $fields);
|
||||
|
||||
$i = 0;
|
||||
foreach($members as $member) {
|
||||
if($i > $count) {
|
||||
break;
|
||||
}
|
||||
|
||||
$arr->items[] = (object) [
|
||||
"id" => $member->getId(),
|
||||
"first_name" => $member->getFirstName(),
|
||||
"last_name" => $member->getLastName(),
|
||||
];
|
||||
|
||||
foreach($filds as $fild) {
|
||||
switch($fild) {
|
||||
case "bdate":
|
||||
$arr->items[$i]->bdate = $member->getBirthday()->format('%e.%m.%Y');
|
||||
break;
|
||||
case "can_post":
|
||||
$arr->items[$i]->can_post = $club->canBeModifiedBy($member);
|
||||
break;
|
||||
case "can_see_all_posts":
|
||||
$arr->items[$i]->can_see_all_posts = 1;
|
||||
break;
|
||||
case "can_see_audio":
|
||||
$arr->items[$i]->can_see_audio = 0;
|
||||
break;
|
||||
case "can_write_private_message":
|
||||
$arr->items[$i]->can_write_private_message = 0;
|
||||
break;
|
||||
case "common_count":
|
||||
$arr->items[$i]->common_count = 420;
|
||||
break;
|
||||
case "connections":
|
||||
$arr->items[$i]->connections = 1;
|
||||
break;
|
||||
case "contacts":
|
||||
$arr->items[$i]->contacts = $member->getContactEmail();
|
||||
break;
|
||||
case "country":
|
||||
$arr->items[$i]->country = 1;
|
||||
break;
|
||||
case "domain":
|
||||
$arr->items[$i]->domain = "";
|
||||
break;
|
||||
case "education":
|
||||
$arr->items[$i]->education = "";
|
||||
break;
|
||||
case "has_mobile":
|
||||
$arr->items[$i]->has_mobile = false;
|
||||
break;
|
||||
case "last_seen":
|
||||
$arr->items[$i]->last_seen = $member->getOnline()->timestamp();
|
||||
break;
|
||||
case "lists":
|
||||
$arr->items[$i]->lists = "";
|
||||
break;
|
||||
case "online":
|
||||
$arr->items[$i]->online = $member->isOnline();
|
||||
break;
|
||||
case "online_mobile":
|
||||
$arr->items[$i]->online_mobile = $member->getOnlinePlatform() == "android" || $member->getOnlinePlatform() == "iphone" || $member->getOnlinePlatform() == "mobile";
|
||||
break;
|
||||
case "photo_100":
|
||||
$arr->items[$i]->photo_100 = $member->getAvatarURL("tiny");
|
||||
break;
|
||||
case "photo_200":
|
||||
$arr->items[$i]->photo_200 = $member->getAvatarURL("normal");
|
||||
break;
|
||||
case "photo_200_orig":
|
||||
$arr->items[$i]->photo_200_orig = $member->getAvatarURL("normal");
|
||||
break;
|
||||
case "photo_400_orig":
|
||||
$arr->items[$i]->photo_400_orig = $member->getAvatarURL("normal");
|
||||
break;
|
||||
case "photo_max":
|
||||
$arr->items[$i]->photo_max = $member->getAvatarURL("original");
|
||||
break;
|
||||
case "photo_max_orig":
|
||||
$arr->items[$i]->photo_max_orig = $member->getAvatarURL();
|
||||
break;
|
||||
case "relation":
|
||||
$arr->items[$i]->relation = $member->getMaritalStatus();
|
||||
break;
|
||||
case "relatives":
|
||||
$arr->items[$i]->relatives = 0;
|
||||
break;
|
||||
case "schools":
|
||||
$arr->items[$i]->schools = 0;
|
||||
break;
|
||||
case "sex":
|
||||
$arr->items[$i]->sex = $member->isFemale() ? 1 : 2;
|
||||
break;
|
||||
case "site":
|
||||
$arr->items[$i]->site = $member->getWebsite();
|
||||
break;
|
||||
case "status":
|
||||
$arr->items[$i]->status = $member->getStatus();
|
||||
break;
|
||||
case "universities":
|
||||
$arr->items[$i]->universities = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
$i++;
|
||||
}
|
||||
return $arr;
|
||||
}
|
||||
|
||||
function getSettings(string $group_id)
|
||||
{
|
||||
$this->requireUser();
|
||||
$club = (new ClubsRepo)->get((int)$group_id);
|
||||
|
||||
if(!$club || !$club->canBeModifiedBy($this->getUser()))
|
||||
$this->fail(15, "You can't get settings of this group.");
|
||||
|
||||
$arr = (object) [
|
||||
"title" => $club->getName(),
|
||||
"description" => $club->getDescription() != NULL ? $club->getDescription() : "",
|
||||
"address" => $club->getShortcode(),
|
||||
"wall" => $club->canPost() == true ? 1 : 0,
|
||||
"photos" => 1,
|
||||
"video" => 0,
|
||||
"audio" => 0,
|
||||
"docs" => 0,
|
||||
"topics" => $club->isEveryoneCanCreateTopics() == true ? 1 : 0,
|
||||
"wiki" => 0,
|
||||
"messages" => 0,
|
||||
"obscene_filter" => 0,
|
||||
"obscene_stopwords" => 0,
|
||||
"obscene_words" => "",
|
||||
"access" => 1,
|
||||
"subject" => 1,
|
||||
"subject_list" => [
|
||||
0 => "в",
|
||||
1 => "опенвк",
|
||||
2 => "нет",
|
||||
3 => "категорий",
|
||||
4 => "групп",
|
||||
],
|
||||
"rss" => "/club".$club->getId()."/rss",
|
||||
"website" => $club->getWebsite(),
|
||||
"age_limits" => 0,
|
||||
"market" => [],
|
||||
];
|
||||
|
||||
return $arr;
|
||||
}
|
||||
|
||||
function isMember(string $group_id, int $user_id, string $user_ids = "", bool $extended = false)
|
||||
{
|
||||
$this->requireUser();
|
||||
$id = $user_id != NULL ? $user_id : explode(",", $user_ids);
|
||||
|
||||
if($group_id < 0)
|
||||
$this->fail(228, "Remove the minus from group_id");
|
||||
|
||||
$club = (new ClubsRepo)->get((int)$group_id);
|
||||
$usver = (new UsersRepo)->get((int)$id);
|
||||
|
||||
if(!$club || $group_id == 0)
|
||||
$this->fail(203, "Invalid club");
|
||||
|
||||
if(!$usver || $usver->isDeleted() || $user_id == 0)
|
||||
$this->fail(30, "Invalid user");
|
||||
|
||||
if($extended == false) {
|
||||
return $club->getSubscriptionStatus($usver) ? 1 : 0;
|
||||
} else {
|
||||
return (object)
|
||||
[
|
||||
"member" => $club->getSubscriptionStatus($usver) ? 1 : 0,
|
||||
"request" => 0,
|
||||
"invitation" => 0,
|
||||
"can_invite" => 0,
|
||||
"can_recall" => 0
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
function remove(int $group_id, int $user_id)
|
||||
{
|
||||
$this->requireUser();
|
||||
|
||||
$this->fail(501, "Not implemented");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ final class Likes extends VKAPIRequestHandler
|
|||
function add(string $type, int $owner_id, int $item_id): object
|
||||
{
|
||||
$this->requireUser();
|
||||
$this->willExecuteWriteAction();
|
||||
|
||||
switch($type) {
|
||||
case "post":
|
||||
|
@ -28,6 +29,7 @@ final class Likes extends VKAPIRequestHandler
|
|||
function delete(string $type, int $owner_id, int $item_id): object
|
||||
{
|
||||
$this->requireUser();
|
||||
$this->willExecuteWriteAction();
|
||||
|
||||
switch($type) {
|
||||
case "post":
|
||||
|
@ -52,11 +54,7 @@ final class Likes extends VKAPIRequestHandler
|
|||
case "post":
|
||||
$user = (new UsersRepo)->get($user_id);
|
||||
if (is_null($user))
|
||||
return (object) [
|
||||
"liked" => 0,
|
||||
"copied" => 0,
|
||||
"sex" => 0
|
||||
];
|
||||
$this->fail(100, "One of the parameters specified was missing or invalid: user not found");
|
||||
|
||||
$post = (new PostsRepo)->getPostById($owner_id, $item_id);
|
||||
if (is_null($post))
|
||||
|
|
|
@ -65,10 +65,16 @@ final class Messages extends VKAPIRequestHandler
|
|||
];
|
||||
}
|
||||
|
||||
function send(int $user_id = -1, int $peer_id = -1, string $domain = "", int $chat_id = -1, string $user_ids = "", string $message = "", int $sticker_id = -1)
|
||||
function send(int $user_id = -1, int $peer_id = -1, string $domain = "", int $chat_id = -1, string $user_ids = "", string $message = "", int $sticker_id = -1, int $forGodSakePleaseDoNotReportAboutMyOnlineActivity = 0)
|
||||
{
|
||||
$this->requireUser();
|
||||
|
||||
$this->willExecuteWriteAction();
|
||||
|
||||
if($forGodSakePleaseDoNotReportAboutMyOnlineActivity == 0)
|
||||
{
|
||||
$this->getUser()->updOnline($this->getPlatform());
|
||||
}
|
||||
|
||||
if($chat_id !== -1)
|
||||
$this->fail(946, "Chats are not implemented");
|
||||
else if($sticker_id !== -1)
|
||||
|
@ -117,6 +123,7 @@ final class Messages extends VKAPIRequestHandler
|
|||
function delete(string $message_ids, int $spam = 0, int $delete_for_all = 0): object
|
||||
{
|
||||
$this->requireUser();
|
||||
$this->willExecuteWriteAction();
|
||||
|
||||
$msgs = new MSGRepo;
|
||||
$ids = preg_split("%, ?%", $message_ids);
|
||||
|
@ -136,6 +143,7 @@ final class Messages extends VKAPIRequestHandler
|
|||
function restore(int $message_id): int
|
||||
{
|
||||
$this->requireUser();
|
||||
$this->willExecuteWriteAction();
|
||||
|
||||
$msg = (new MSGRepo)->get($message_id);
|
||||
if(!$msg)
|
||||
|
|
|
@ -7,9 +7,14 @@ use openvk\VKAPI\Handlers\Wall;
|
|||
|
||||
final class Newsfeed extends VKAPIRequestHandler
|
||||
{
|
||||
function get(string $fields = "", int $start_from = 0, int $offset = 0, int $count = 30, int $extended = 0)
|
||||
function get(string $fields = "", int $start_from = 0, int $start_time = 0, int $end_time = 0, int $offset = 0, int $count = 30, int $extended = 0, int $forGodSakePleaseDoNotReportAboutMyOnlineActivity = 0)
|
||||
{
|
||||
$this->requireUser();
|
||||
|
||||
if($forGodSakePleaseDoNotReportAboutMyOnlineActivity == 0)
|
||||
{
|
||||
$this->getUser()->updOnline($this->getPlatform());
|
||||
}
|
||||
|
||||
$id = $this->getUser()->getId();
|
||||
$subs = DatabaseConnection::i()
|
||||
|
@ -28,6 +33,8 @@ final class Newsfeed extends VKAPIRequestHandler
|
|||
->where("wall IN (?)", $ids)
|
||||
->where("deleted", 0)
|
||||
->where("id < (?)", empty($start_from) ? PHP_INT_MAX : $start_from)
|
||||
->where("? <= created", empty($start_time) ? 0 : $start_time)
|
||||
->where("? >= created", empty($end_time) ? PHP_INT_MAX : $end_time)
|
||||
->order("created DESC");
|
||||
|
||||
$rposts = [];
|
||||
|
@ -40,7 +47,7 @@ final class Newsfeed extends VKAPIRequestHandler
|
|||
return $response;
|
||||
}
|
||||
|
||||
function getGlobal(string $fields = "", int $start_from = 0, int $offset = 0, int $count = 30, int $extended = 0)
|
||||
function getGlobal(string $fields = "", int $start_from = 0, int $start_time = 0, int $end_time = 0, int $offset = 0, int $count = 30, int $extended = 0)
|
||||
{
|
||||
$this->requireUser();
|
||||
|
||||
|
@ -50,7 +57,9 @@ final class Newsfeed extends VKAPIRequestHandler
|
|||
$queryBase .= " AND `nsfw` = 0";
|
||||
|
||||
$start_from = empty($start_from) ? PHP_INT_MAX : $start_from;
|
||||
$posts = DatabaseConnection::i()->getConnection()->query("SELECT `posts`.`id` " . $queryBase . " AND `posts`.`id` < " . $start_from . " ORDER BY `created` DESC LIMIT " . $count . " OFFSET " . $offset);
|
||||
$start_time = empty($start_time) ? 0 : $start_time;
|
||||
$end_time = empty($end_time) ? PHP_INT_MAX : $end_time;
|
||||
$posts = DatabaseConnection::i()->getConnection()->query("SELECT `posts`.`id` " . $queryBase . " AND `posts`.`id` <= " . $start_from . " AND " . $start_time . " <= `posts`.`created` AND `posts`.`created` <= " . $end_time . " ORDER BY `created` DESC LIMIT " . $count . " OFFSET " . $offset);
|
||||
|
||||
$rposts = [];
|
||||
$ids = [];
|
||||
|
|
283
VKAPI/Handlers/Notes.php
Normal file
283
VKAPI/Handlers/Notes.php
Normal file
|
@ -0,0 +1,283 @@
|
|||
<?php declare(strict_types=1);
|
||||
namespace openvk\VKAPI\Handlers;
|
||||
use openvk\Web\Models\Repositories\Notes as NotesRepo;
|
||||
use openvk\Web\Models\Repositories\Users as UsersRepo;
|
||||
use openvk\Web\Models\Repositories\Comments as CommentsRepo;
|
||||
use openvk\Web\Models\Repositories\Photos as PhotosRepo;
|
||||
use openvk\Web\Models\Repositories\Videos as VideosRepo;
|
||||
use openvk\Web\Models\Entities\{Note, Comment};
|
||||
|
||||
final class Notes extends VKAPIRequestHandler
|
||||
{
|
||||
function add(string $title, string $text, int $privacy = 0, int $comment_privacy = 0, string $privacy_view = "", string $privacy_comment = "")
|
||||
{
|
||||
$this->requireUser();
|
||||
$this->willExecuteWriteAction();
|
||||
|
||||
$note = new Note;
|
||||
$note->setOwner($this->getUser()->getId());
|
||||
$note->setCreated(time());
|
||||
$note->setName($title);
|
||||
$note->setSource($text);
|
||||
$note->setEdited(time());
|
||||
$note->save();
|
||||
|
||||
return $note->getVirtualId();
|
||||
}
|
||||
|
||||
function createComment(string $note_id, int $owner_id, string $message, int $reply_to = 0, string $attachments = "")
|
||||
{
|
||||
$this->requireUser();
|
||||
$this->willExecuteWriteAction();
|
||||
$note = (new NotesRepo)->getNoteById((int)$owner_id, (int)$note_id);
|
||||
|
||||
if(!$note)
|
||||
$this->fail(180, "Note not found");
|
||||
|
||||
if($note->isDeleted())
|
||||
$this->fail(189, "Note is deleted");
|
||||
|
||||
if($note->getOwner()->isDeleted())
|
||||
$this->fail(403, "Owner is deleted");
|
||||
|
||||
if(!$note->getOwner()->getPrivacyPermission('notes.read', $this->getUser()))
|
||||
$this->fail(43, "No access");
|
||||
|
||||
if(empty($message) && empty($attachments))
|
||||
$this->fail(100, "Required parameter 'message' missing.");
|
||||
|
||||
$comment = new Comment;
|
||||
$comment->setOwner($this->getUser()->getId());
|
||||
$comment->setModel(get_class($note));
|
||||
$comment->setTarget($note->getId());
|
||||
$comment->setContent($message);
|
||||
$comment->setCreated(time());
|
||||
$comment->save();
|
||||
|
||||
if(!empty($attachments)) {
|
||||
$attachmentsArr = explode(",", $attachments);
|
||||
|
||||
if(sizeof($attachmentsArr) > 10)
|
||||
$this->fail(50, "Error: too many attachments");
|
||||
|
||||
foreach($attachmentsArr as $attac) {
|
||||
$attachmentType = NULL;
|
||||
|
||||
if(str_contains($attac, "photo"))
|
||||
$attachmentType = "photo";
|
||||
elseif(str_contains($attac, "video"))
|
||||
$attachmentType = "video";
|
||||
else
|
||||
$this->fail(205, "Unknown attachment type");
|
||||
|
||||
$attachment = str_replace($attachmentType, "", $attac);
|
||||
|
||||
$attachmentOwner = (int)explode("_", $attachment)[0];
|
||||
$attachmentId = (int)end(explode("_", $attachment));
|
||||
|
||||
$attacc = NULL;
|
||||
|
||||
if($attachmentType == "photo") {
|
||||
$attacc = (new PhotosRepo)->getByOwnerAndVID($attachmentOwner, $attachmentId);
|
||||
if(!$attacc || $attacc->isDeleted())
|
||||
$this->fail(100, "Photo does not exists");
|
||||
if($attacc->getOwner()->getId() != $this->getUser()->getId())
|
||||
$this->fail(43, "You do not have access to this photo");
|
||||
|
||||
$comment->attach($attacc);
|
||||
} elseif($attachmentType == "video") {
|
||||
$attacc = (new VideosRepo)->getByOwnerAndVID($attachmentOwner, $attachmentId);
|
||||
if(!$attacc || $attacc->isDeleted())
|
||||
$this->fail(100, "Video does not exists");
|
||||
if($attacc->getOwner()->getId() != $this->getUser()->getId())
|
||||
$this->fail(43, "You do not have access to this video");
|
||||
|
||||
$comment->attach($attacc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $comment->getId();
|
||||
}
|
||||
|
||||
function delete(string $note_id)
|
||||
{
|
||||
$this->requireUser();
|
||||
$this->willExecuteWriteAction();
|
||||
|
||||
$note = (new NotesRepo)->get((int)$note_id);
|
||||
|
||||
if(!$note)
|
||||
$this->fail(180, "Note not found");
|
||||
|
||||
if(!$note->canBeModifiedBy($this->getUser()))
|
||||
$this->fail(15, "Access to note denied");
|
||||
|
||||
$note->delete();
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
function deleteComment(int $comment_id, int $owner_id = 0)
|
||||
{
|
||||
$this->requireUser();
|
||||
$this->willExecuteWriteAction();
|
||||
|
||||
$comment = (new CommentsRepo)->get($comment_id);
|
||||
|
||||
if(!$comment || !$comment->canBeDeletedBy($this->getUser()))
|
||||
$this->fail(403, "Access to comment denied");
|
||||
|
||||
$comment->delete();
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
function edit(string $note_id, string $title = "", string $text = "", int $privacy = 0, int $comment_privacy = 0, string $privacy_view = "", string $privacy_comment = "")
|
||||
{
|
||||
$this->requireUser();
|
||||
$this->willExecuteWriteAction();
|
||||
|
||||
$note = (new NotesRepo)->getNoteById($this->getUser()->getId(), (int)$note_id);
|
||||
|
||||
if(!$note)
|
||||
$this->fail(180, "Note not found");
|
||||
|
||||
if($note->isDeleted())
|
||||
$this->fail(189, "Note is deleted");
|
||||
|
||||
if(!$note->canBeModifiedBy($this->getUser()))
|
||||
$this->fail(403, "No access");
|
||||
|
||||
!empty($title) ? $note->setName($title) : NULL;
|
||||
!empty($text) ? $note->setSource($text) : NULL;
|
||||
|
||||
$note->setCached_Content(NULL);
|
||||
$note->setEdited(time());
|
||||
$note->save();
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
function editComment(int $comment_id, string $message, int $owner_id = NULL)
|
||||
{
|
||||
/*
|
||||
$this->requireUser();
|
||||
$this->willExecuteWriteAction();
|
||||
|
||||
$comment = (new CommentsRepo)->get($comment_id);
|
||||
|
||||
if($comment->getOwner() != $this->getUser()->getId())
|
||||
$this->fail(15, "Access to comment denied");
|
||||
|
||||
$comment->setContent($message);
|
||||
$comment->setEdited(time());
|
||||
$comment->save();
|
||||
*/
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
function get(int $user_id, string $note_ids = "", int $offset = 0, int $count = 10, int $sort = 0)
|
||||
{
|
||||
$this->requireUser();
|
||||
$user = (new UsersRepo)->get($user_id);
|
||||
|
||||
if(!$user || $user->isDeleted())
|
||||
$this->fail(15, "Invalid user");
|
||||
|
||||
if(!$user->getPrivacyPermission('notes.read', $this->getUser()))
|
||||
$this->fail(43, "Access denied: this user chose to hide his notes");
|
||||
|
||||
if(empty($note_ids)) {
|
||||
$notes = array_slice(iterator_to_array((new NotesRepo)->getUserNotes($user, 1, $count + $offset, $sort == 0 ? "ASC" : "DESC")), $offset);
|
||||
$nodez = (object) [
|
||||
"count" => (new NotesRepo)->getUserNotesCount((new UsersRepo)->get($user_id)),
|
||||
"notes" => []
|
||||
];
|
||||
|
||||
foreach($notes as $note) {
|
||||
if($note->isDeleted()) continue;
|
||||
|
||||
$nodez->notes[] = $note->toVkApiStruct();
|
||||
}
|
||||
} else {
|
||||
$notes = explode(',', $note_ids);
|
||||
|
||||
foreach($notes as $note)
|
||||
{
|
||||
$id = explode("_", $note);
|
||||
|
||||
$items = [];
|
||||
|
||||
$note = (new NotesRepo)->getNoteById((int)$id[0], (int)$id[1]);
|
||||
if($note && !$note->isDeleted()) {
|
||||
$nodez->notes[] = $note->toVkApiStruct();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $nodez;
|
||||
}
|
||||
|
||||
function getById(int $note_id, int $owner_id, bool $need_wiki = false)
|
||||
{
|
||||
$this->requireUser();
|
||||
|
||||
$note = (new NotesRepo)->getNoteById($owner_id, $note_id);
|
||||
|
||||
if(!$note)
|
||||
$this->fail(180, "Note not found");
|
||||
|
||||
if($note->isDeleted())
|
||||
$this->fail(189, "Note is deleted");
|
||||
|
||||
if(!$note->getOwner() || $note->getOwner()->isDeleted())
|
||||
$this->fail(177, "Owner does not exists");
|
||||
|
||||
if(!$note->getOwner()->getPrivacyPermission('notes.read', $this->getUser()))
|
||||
$this->fail(40, "Access denied: this user chose to hide his notes");
|
||||
|
||||
return $note->toVkApiStruct();
|
||||
}
|
||||
|
||||
function getComments(int $note_id, int $owner_id, int $sort = 1, int $offset = 0, int $count = 100)
|
||||
{
|
||||
$this->requireUser();
|
||||
|
||||
$note = (new NotesRepo)->getNoteById($owner_id, $note_id);
|
||||
|
||||
if(!$note)
|
||||
$this->fail(180, "Note not found");
|
||||
|
||||
if($note->isDeleted())
|
||||
$this->fail(189, "Note is deleted");
|
||||
|
||||
if(!$note->getOwner())
|
||||
$this->fail(177, "Owner does not exists");
|
||||
|
||||
if(!$note->getOwner()->getPrivacyPermission('notes.read', $this->getUser()))
|
||||
$this->fail(14, "No access");
|
||||
|
||||
$arr = (object) [
|
||||
"count" => $note->getCommentsCount(),
|
||||
"comments" => []];
|
||||
$comments = array_slice(iterator_to_array($note->getComments(1, $count + $offset)), $offset);
|
||||
|
||||
foreach($comments as $comment) {
|
||||
$arr->comments[] = $comment->toVkApiStruct($this->getUser(), false, false, $note);
|
||||
}
|
||||
|
||||
return $arr;
|
||||
}
|
||||
|
||||
function getFriendsNotes(int $offset = 0, int $count = 0)
|
||||
{
|
||||
$this->fail(501, "Not implemented");
|
||||
}
|
||||
|
||||
function restoreComment(int $comment_id = 0, int $owner_id = 0)
|
||||
{
|
||||
$this->fail(501, "Not implemented");
|
||||
}
|
||||
}
|
|
@ -3,9 +3,12 @@ namespace openvk\VKAPI\Handlers;
|
|||
|
||||
use Nette\InvalidStateException;
|
||||
use Nette\Utils\ImageException;
|
||||
use openvk\Web\Models\Entities\Photo;
|
||||
use openvk\Web\Models\Entities\{Photo, Album, Comment};
|
||||
use openvk\Web\Models\Repositories\Albums;
|
||||
use openvk\Web\Models\Repositories\Photos as PhotosRepo;
|
||||
use openvk\Web\Models\Repositories\Clubs;
|
||||
use openvk\Web\Models\Repositories\Users as UsersRepo;
|
||||
use openvk\Web\Models\Repositories\Comments as CommentsRepo;
|
||||
|
||||
final class Photos extends VKAPIRequestHandler
|
||||
{
|
||||
|
@ -58,7 +61,7 @@ final class Photos extends VKAPIRequestHandler
|
|||
}
|
||||
|
||||
return (object) [
|
||||
"upload_url" => $this->getPhotoUploadUrl("photo", isset($club) ? 0 : $club->getId()),
|
||||
"upload_url" => $this->getPhotoUploadUrl("photo", !isset($club) ? 0 : $club->getId()),
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -227,4 +230,504 @@ final class Photos extends VKAPIRequestHandler
|
|||
"items" => $images,
|
||||
];
|
||||
}
|
||||
|
||||
function createAlbum(string $title, int $group_id = 0, string $description = "", int $privacy = 0)
|
||||
{
|
||||
$this->requireUser();
|
||||
$this->willExecuteWriteAction();
|
||||
|
||||
if($group_id != 0) {
|
||||
$club = (new Clubs)->get((int) $group_id);
|
||||
|
||||
if(!$club || !$club->canBeModifiedBy($this->getUser())) {
|
||||
$this->fail(20, "Invalid club");
|
||||
}
|
||||
}
|
||||
|
||||
$album = new Album;
|
||||
$album->setOwner(isset($club) ? $club->getId() * -1 : $this->getUser()->getId());
|
||||
$album->setName($title);
|
||||
$album->setDescription($description);
|
||||
$album->setCreated(time());
|
||||
$album->save();
|
||||
|
||||
return $album->toVkApiStruct($this->getUser());
|
||||
}
|
||||
|
||||
function editAlbum(int $album_id, int $owner_id, string $title, string $description = "", int $privacy = 0)
|
||||
{
|
||||
$this->requireUser();
|
||||
$this->willExecuteWriteAction();
|
||||
|
||||
$album = (new Albums)->getAlbumByOwnerAndId($owner_id, $album_id);
|
||||
|
||||
if(!$album || $album->isDeleted()) {
|
||||
$this->fail(2, "Invalid album");
|
||||
}
|
||||
|
||||
if(empty($title)) {
|
||||
$this->fail(25, "Title is empty");
|
||||
}
|
||||
|
||||
if($album->isCreatedBySystem()) {
|
||||
$this->fail(40, "You can't change system album");
|
||||
}
|
||||
|
||||
if(!$album->canBeModifiedBy($this->getUser())) {
|
||||
$this->fail(2, "Access to album denied");
|
||||
}
|
||||
|
||||
$album->setName($title);
|
||||
$album->setDescription($description);
|
||||
|
||||
$album->save();
|
||||
|
||||
return $album->toVkApiStruct($this->getUser());
|
||||
}
|
||||
|
||||
function getAlbums(int $owner_id, string $album_ids = "", int $offset = 0, int $count = 100, bool $need_system = true, bool $need_covers = true, bool $photo_sizes = false)
|
||||
{
|
||||
$this->requireUser();
|
||||
$this->willExecuteWriteAction();
|
||||
|
||||
$res = [];
|
||||
|
||||
if(empty($album_ids)) {
|
||||
if($owner_id > 0) {
|
||||
$user = (new UsersRepo)->get($owner_id);
|
||||
|
||||
$res = [
|
||||
"count" => (new Albums)->getUserAlbumsCount($user),
|
||||
"items" => []
|
||||
];
|
||||
|
||||
if(!$user || $user->isDeleted())
|
||||
$this->fail(2, "Invalid user");
|
||||
|
||||
|
||||
if(!$user->getPrivacyPermission('photos.read', $this->getUser()))
|
||||
$this->fail(21, "This user chose to hide his albums.");
|
||||
|
||||
$albums = array_slice(iterator_to_array((new Albums)->getUserAlbums($user, 1, $count + $offset)), $offset);
|
||||
|
||||
foreach($albums as $album) {
|
||||
if(!$need_system && $album->isCreatedBySystem()) continue;
|
||||
$res["items"][] = $album->toVkApiStruct($this->getUser(), $need_covers, $photo_sizes);
|
||||
}
|
||||
}
|
||||
|
||||
else {
|
||||
$club = (new Clubs)->get($owner_id * -1);
|
||||
|
||||
$res = [
|
||||
"count" => (new Albums)->getClubAlbumsCount($club),
|
||||
"items" => []
|
||||
];
|
||||
|
||||
if(!$club)
|
||||
$this->fail(2, "Invalid club");
|
||||
|
||||
$albums = array_slice(iterator_to_array((new Albums)->getClubAlbums($club, 1, $count + $offset)), $offset);
|
||||
|
||||
foreach($albums as $album) {
|
||||
if(!$need_system && $album->isCreatedBySystem()) continue;
|
||||
$res["items"][] = $album->toVkApiStruct($this->getUser(), $need_covers, $photo_sizes);
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
$albums = explode(',', $album_ids);
|
||||
|
||||
$res = [
|
||||
"count" => sizeof($albums),
|
||||
"items" => []
|
||||
];
|
||||
|
||||
foreach($albums as $album)
|
||||
{
|
||||
$id = explode("_", $album);
|
||||
|
||||
$album = (new Albums)->getAlbumByOwnerAndId((int)$id[0], (int)$id[1]);
|
||||
if($album && !$album->isDeleted()) {
|
||||
if(!$need_system && $album->isCreatedBySystem()) continue;
|
||||
$res["items"][] = $album->toVkApiStruct($this->getUser(), $need_covers, $photo_sizes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $res;
|
||||
}
|
||||
|
||||
function getAlbumsCount(int $user_id = 0, int $group_id = 0)
|
||||
{
|
||||
$this->requireUser();
|
||||
$this->willExecuteWriteAction();
|
||||
|
||||
if($user_id == 0 && $group_id == 0 || $user_id > 0 && $group_id > 0) {
|
||||
$this->fail(21, "Select user_id or group_id");
|
||||
}
|
||||
|
||||
if($user_id > 0) {
|
||||
|
||||
$us = (new UsersRepo)->get($user_id);
|
||||
if(!$us || $us->isDeleted()) {
|
||||
$this->fail(21, "Invalid user");
|
||||
}
|
||||
|
||||
if(!$us->getPrivacyPermission('photos.read', $this->getUser())) {
|
||||
$this->fail(21, "This user chose to hide his albums.");
|
||||
}
|
||||
|
||||
return (new Albums)->getUserAlbumsCount($us);
|
||||
}
|
||||
|
||||
if($group_id > 0)
|
||||
{
|
||||
$cl = (new Clubs)->get($group_id);
|
||||
if(!$cl) {
|
||||
$this->fail(21, "Invalid club");
|
||||
}
|
||||
|
||||
return (new Albums)->getClubAlbumsCount($cl);
|
||||
}
|
||||
}
|
||||
|
||||
function getById(string $photos, bool $extended = false, bool $photo_sizes = false)
|
||||
{
|
||||
$this->requireUser();
|
||||
$this->willExecuteWriteAction();
|
||||
|
||||
$phts = explode(",", $photos);
|
||||
$res = [];
|
||||
|
||||
foreach($phts as $phota) {
|
||||
$ph = explode("_", $phota);
|
||||
$photo = (new PhotosRepo)->getByOwnerAndVID((int)$ph[0], (int)$ph[1]);
|
||||
|
||||
if(!$photo || $photo->isDeleted()) {
|
||||
$this->fail(21, "Invalid photo");
|
||||
}
|
||||
|
||||
if($photo->getOwner()->isDeleted()) {
|
||||
$this->fail(21, "Owner of this photo is deleted");
|
||||
}
|
||||
|
||||
if(!$photo->getOwner()->getPrivacyPermission('photos.read', $this->getUser())) {
|
||||
$this->fail(21, "This user chose to hide his photos.");
|
||||
}
|
||||
|
||||
$res[] = $photo->toVkApiStruct($photo_sizes, $extended);
|
||||
}
|
||||
|
||||
return $res;
|
||||
}
|
||||
|
||||
function get(int $owner_id, int $album_id, string $photo_ids = "", bool $extended = false, bool $photo_sizes = false, int $offset = 0, int $count = 10)
|
||||
{
|
||||
$this->requireUser();
|
||||
$this->willExecuteWriteAction();
|
||||
|
||||
$res = [];
|
||||
|
||||
if(empty($photo_ids)) {
|
||||
$album = (new Albums)->getAlbumByOwnerAndId($owner_id, $album_id);
|
||||
|
||||
if(!$album->getOwner()->getPrivacyPermission('photos.read', $this->getUser())) {
|
||||
$this->fail(21, "This user chose to hide his albums.");
|
||||
}
|
||||
|
||||
if(!$album || $album->isDeleted()) {
|
||||
$this->fail(21, "Invalid album");
|
||||
}
|
||||
|
||||
$photos = array_slice(iterator_to_array($album->getPhotos(1, $count + $offset)), $offset);
|
||||
$res["count"] = sizeof($photos);
|
||||
|
||||
foreach($photos as $photo) {
|
||||
if(!$photo || $photo->isDeleted()) continue;
|
||||
$res["items"][] = $photo->toVkApiStruct($photo_sizes, $extended);
|
||||
}
|
||||
|
||||
} else {
|
||||
$photos = explode(',', $photo_ids);
|
||||
|
||||
$res = [
|
||||
"count" => sizeof($photos),
|
||||
"items" => []
|
||||
];
|
||||
|
||||
foreach($photos as $photo)
|
||||
{
|
||||
$id = explode("_", $photo);
|
||||
|
||||
$phot = (new PhotosRepo)->getByOwnerAndVID((int)$id[0], (int)$id[1]);
|
||||
if($phot && !$phot->isDeleted()) {
|
||||
$res["items"][] = $phot->toVkApiStruct($photo_sizes, $extended);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $res;
|
||||
}
|
||||
|
||||
function deleteAlbum(int $album_id, int $group_id = 0)
|
||||
{
|
||||
$this->requireUser();
|
||||
$this->willExecuteWriteAction();
|
||||
|
||||
$album = (new Albums)->get($album_id);
|
||||
|
||||
if(!$album || $album->canBeModifiedBy($this->getUser())) {
|
||||
$this->fail(21, "Invalid album");
|
||||
}
|
||||
|
||||
if($album->isDeleted()) {
|
||||
$this->fail(22, "Album already deleted");
|
||||
}
|
||||
|
||||
$album->delete();
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
function edit(int $owner_id, int $photo_id, string $caption = "")
|
||||
{
|
||||
$this->requireUser();
|
||||
$this->willExecuteWriteAction();
|
||||
|
||||
$photo = (new PhotosRepo)->getByOwnerAndVID($owner_id, $photo_id);
|
||||
|
||||
if(!$photo) {
|
||||
$this->fail(21, "Invalid photo");
|
||||
}
|
||||
|
||||
if($photo->isDeleted()) {
|
||||
$this->fail(21, "Photo is deleted");
|
||||
}
|
||||
|
||||
if(!empty($caption)) {
|
||||
$photo->setDescription($caption);
|
||||
$photo->save();
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
function delete(int $owner_id, int $photo_id, string $photos = "")
|
||||
{
|
||||
$this->requireUser();
|
||||
$this->willExecuteWriteAction();
|
||||
|
||||
if(empty($photos)) {
|
||||
$photo = (new PhotosRepo)->getByOwnerAndVID($owner_id, $photo_id);
|
||||
|
||||
if($this->getUser()->getId() !== $photo->getOwner()->getId()) {
|
||||
$this->fail(21, "You can't delete another's photo");
|
||||
}
|
||||
|
||||
if(!$photo) {
|
||||
$this->fail(21, "Invalid photo");
|
||||
}
|
||||
|
||||
if($photo->isDeleted()) {
|
||||
$this->fail(21, "Photo already deleted");
|
||||
}
|
||||
|
||||
$photo->delete();
|
||||
} else {
|
||||
$photozs = explode(',', $photos);
|
||||
|
||||
foreach($photozs as $photo)
|
||||
{
|
||||
$id = explode("_", $photo);
|
||||
|
||||
$phot = (new PhotosRepo)->getByOwnerAndVID((int)$id[0], (int)$id[1]);
|
||||
|
||||
if($this->getUser()->getId() !== $phot->getOwner()->getId()) {
|
||||
$this->fail(21, "You can't delete another's photo");
|
||||
}
|
||||
|
||||
if(!$phot) {
|
||||
$this->fail(21, "Invalid photo");
|
||||
}
|
||||
|
||||
if($phot->isDeleted()) {
|
||||
$this->fail(21, "Photo already deleted");
|
||||
}
|
||||
|
||||
$phot->delete();
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
function getAllComments(int $owner_id, int $album_id, bool $need_likes = false, int $offset = 0, int $count = 100)
|
||||
{
|
||||
$this->fail(501, "Not implemented");
|
||||
}
|
||||
|
||||
function deleteComment(int $comment_id, int $owner_id = 0)
|
||||
{
|
||||
$this->requireUser();
|
||||
$this->willExecuteWriteAction();
|
||||
|
||||
$comment = (new CommentsRepo)->get($comment_id);
|
||||
if(!$comment) {
|
||||
$this->fail(21, "Invalid comment");
|
||||
}
|
||||
|
||||
if(!$comment->canBeModifiedBy($this->getUser())) {
|
||||
$this->fail(21, "Forbidden");
|
||||
}
|
||||
|
||||
if($comment->isDeleted()) {
|
||||
$this->fail(4, "Comment already deleted");
|
||||
}
|
||||
|
||||
$comment->delete();
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
function createComment(int $owner_id, int $photo_id, string $message = "", string $attachments = "", bool $from_group = false)
|
||||
{
|
||||
$this->requireUser();
|
||||
$this->willExecuteWriteAction();
|
||||
|
||||
if(empty($message) && empty($attachments)) {
|
||||
$this->fail(100, "Required parameter 'message' missing.");
|
||||
}
|
||||
|
||||
$photo = (new PhotosRepo)->getByOwnerAndVID($owner_id, $photo_id);
|
||||
|
||||
if(!$photo->getAlbum()->getOwner()->getPrivacyPermission('photos.read', $this->getUser())) {
|
||||
$this->fail(21, "This user chose to hide his albums.");
|
||||
}
|
||||
|
||||
if(!$photo)
|
||||
$this->fail(180, "Photo not found");
|
||||
if($photo->isDeleted())
|
||||
$this->fail(189, "Photo is deleted");
|
||||
|
||||
$comment = new Comment;
|
||||
$comment->setOwner($this->getUser()->getId());
|
||||
$comment->setModel(get_class($photo));
|
||||
$comment->setTarget($photo->getId());
|
||||
$comment->setContent($message);
|
||||
$comment->setCreated(time());
|
||||
$comment->save();
|
||||
|
||||
if(!empty($attachments)) {
|
||||
$attachmentsArr = explode(",", $attachments);
|
||||
|
||||
if(sizeof($attachmentsArr) > 10)
|
||||
$this->fail(50, "Error: too many attachments");
|
||||
|
||||
foreach($attachmentsArr as $attac) {
|
||||
$attachmentType = NULL;
|
||||
|
||||
if(str_contains($attac, "photo"))
|
||||
$attachmentType = "photo";
|
||||
elseif(str_contains($attac, "video"))
|
||||
$attachmentType = "video";
|
||||
else
|
||||
$this->fail(205, "Unknown attachment type");
|
||||
|
||||
$attachment = str_replace($attachmentType, "", $attac);
|
||||
|
||||
$attachmentOwner = (int)explode("_", $attachment)[0];
|
||||
$attachmentId = (int)end(explode("_", $attachment));
|
||||
|
||||
$attacc = NULL;
|
||||
|
||||
if($attachmentType == "photo") {
|
||||
$attacc = (new PhotosRepo)->getByOwnerAndVID($attachmentOwner, $attachmentId);
|
||||
if(!$attacc || $attacc->isDeleted())
|
||||
$this->fail(100, "Photo does not exists");
|
||||
if($attacc->getOwner()->getId() != $this->getUser()->getId())
|
||||
$this->fail(43, "You do not have access to this photo");
|
||||
|
||||
$comment->attach($attacc);
|
||||
} elseif($attachmentType == "video") {
|
||||
$attacc = (new VideosRepo)->getByOwnerAndVID($attachmentOwner, $attachmentId);
|
||||
if(!$attacc || $attacc->isDeleted())
|
||||
$this->fail(100, "Video does not exists");
|
||||
if($attacc->getOwner()->getId() != $this->getUser()->getId())
|
||||
$this->fail(43, "You do not have access to this video");
|
||||
|
||||
$comment->attach($attacc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $comment->getId();
|
||||
}
|
||||
|
||||
function getAll(int $owner_id, bool $extended = false, int $offset = 0, int $count = 100, bool $photo_sizes = false)
|
||||
{
|
||||
$this->requireUser();
|
||||
$this->willExecuteWriteAction();
|
||||
|
||||
if($owner_id < 0) {
|
||||
$this->fail(4, "This method doesn't works with clubs");
|
||||
}
|
||||
|
||||
$user = (new UsersRepo)->get($owner_id);
|
||||
|
||||
if(!$user) {
|
||||
$this->fail(4, "Invalid user");
|
||||
}
|
||||
|
||||
if(!$user->getPrivacyPermission('photos.read', $this->getUser())) {
|
||||
$this->fail(21, "This user chose to hide his albums.");
|
||||
}
|
||||
|
||||
$photos = array_slice(iterator_to_array((new PhotosRepo)->getEveryUserPhoto($user, 1, $count + $offset)), $offset);
|
||||
$res = [];
|
||||
|
||||
foreach($photos as $photo) {
|
||||
if(!$photo || $photo->isDeleted()) continue;
|
||||
$res["items"][] = $photo->toVkApiStruct($photo_sizes, $extended);
|
||||
}
|
||||
|
||||
return $res;
|
||||
}
|
||||
|
||||
function getComments(int $owner_id, int $photo_id, bool $need_likes = false, int $offset = 0, int $count = 100, bool $extended = false, string $fields = "")
|
||||
{
|
||||
$this->requireUser();
|
||||
$this->willExecuteWriteAction();
|
||||
|
||||
$photo = (new PhotosRepo)->getByOwnerAndVID($owner_id, $photo_id);
|
||||
$comms = array_slice(iterator_to_array($photo->getComments(1, $offset + $count)), $offset);
|
||||
|
||||
if(!$photo) {
|
||||
$this->fail(4, "Invalid photo");
|
||||
}
|
||||
|
||||
if(!$photo->getAlbum()->getOwner()->getPrivacyPermission('photos.read', $this->getUser())) {
|
||||
$this->fail(21, "This user chose to hide his photos.");
|
||||
}
|
||||
|
||||
if($photo->isDeleted()) {
|
||||
$this->fail(4, "Photo is deleted");
|
||||
}
|
||||
|
||||
$res = [
|
||||
"count" => sizeof($comms),
|
||||
"items" => []
|
||||
];
|
||||
|
||||
foreach($comms as $comment) {
|
||||
$res["items"][] = $comment->toVkApiStruct($this->getUser(), $need_likes, $extended);
|
||||
if($extended) {
|
||||
if($comment->getOwner() instanceof \openvk\Web\Models\Entities\User) {
|
||||
$res["profiles"][] = $comment->getOwner()->toVkApiStruct();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $res;
|
||||
}
|
||||
}
|
|
@ -66,6 +66,7 @@ final class Polls extends VKAPIRequestHandler
|
|||
function addVote(int $poll_id, string $answers_ids)
|
||||
{
|
||||
$this->requireUser();
|
||||
$this->willExecuteWriteAction();
|
||||
|
||||
$poll = (new PollsRepo)->get($poll_id);
|
||||
|
||||
|
@ -74,7 +75,7 @@ final class Polls extends VKAPIRequestHandler
|
|||
|
||||
try {
|
||||
$poll->vote($this->getUser(), explode(",", $answers_ids));
|
||||
return 0;
|
||||
return 1;
|
||||
} catch(AlreadyVotedException $ex) {
|
||||
return 0;
|
||||
} catch(PollLockedException $ex) {
|
||||
|
@ -87,6 +88,7 @@ final class Polls extends VKAPIRequestHandler
|
|||
function deleteVote(int $poll_id)
|
||||
{
|
||||
$this->requireUser();
|
||||
$this->willExecuteWriteAction();
|
||||
|
||||
$poll = (new PollsRepo)->get($poll_id);
|
||||
|
||||
|
@ -95,7 +97,7 @@ final class Polls extends VKAPIRequestHandler
|
|||
|
||||
try {
|
||||
$poll->revokeVote($this->getUser());
|
||||
return 0;
|
||||
return 1;
|
||||
} catch(PollLockedException $ex) {
|
||||
$this->fail(15, "Access denied: Poll is locked or isn't revotable");
|
||||
} catch(InvalidOptionException $ex) {
|
||||
|
|
35
VKAPI/Handlers/Status.php
Normal file
35
VKAPI/Handlers/Status.php
Normal file
|
@ -0,0 +1,35 @@
|
|||
<?php declare(strict_types=1);
|
||||
namespace openvk\VKAPI\Handlers;
|
||||
use openvk\Web\Models\Entities\User;
|
||||
use openvk\Web\Models\Repositories\Users as UsersRepo;
|
||||
|
||||
final class Status extends VKAPIRequestHandler
|
||||
{
|
||||
function get(int $user_id = 0, int $group_id = 0)
|
||||
{
|
||||
$this->requireUser();
|
||||
if($user_id == 0 && $group_id == 0) {
|
||||
return $this->getUser()->getStatus();
|
||||
} else {
|
||||
if($group_id > 0)
|
||||
$this->fail(501, "Group statuses are not implemented");
|
||||
else
|
||||
return (new UsersRepo)->get($user_id)->getStatus();
|
||||
}
|
||||
}
|
||||
|
||||
function set(string $text, int $group_id = 0)
|
||||
{
|
||||
$this->requireUser();
|
||||
$this->willExecuteWriteAction();
|
||||
|
||||
if($group_id > 0) {
|
||||
$this->fail(501, "Group statuses are not implemented");
|
||||
} else {
|
||||
$this->getUser()->setStatus($text);
|
||||
$this->getUser()->save();
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -32,7 +32,15 @@ final class Users extends VKAPIRequestHandler
|
|||
"first_name" => "DELETED",
|
||||
"last_name" => "",
|
||||
"deactivated" => "deleted"
|
||||
];
|
||||
];
|
||||
} else if($usr->isBanned()) {
|
||||
$response[$i] = (object)[
|
||||
"id" => $usr->getId(),
|
||||
"first_name" => $usr->getFirstName(),
|
||||
"last_name" => $usr->getLastName(),
|
||||
"deactivated" => "banned",
|
||||
"ban_reason" => $usr->getBanReason()
|
||||
];
|
||||
} else if($usrs[$i] == NULL) {
|
||||
|
||||
} else {
|
||||
|
@ -108,11 +116,31 @@ final class Users extends VKAPIRequestHandler
|
|||
}
|
||||
break;
|
||||
case "last_seen":
|
||||
if ($usr->onlineStatus() == 0)
|
||||
if ($usr->onlineStatus() == 0) {
|
||||
$platform = $usr->getOnlinePlatform(true);
|
||||
switch ($platform) {
|
||||
case 'iphone':
|
||||
$platform = 2;
|
||||
break;
|
||||
|
||||
case 'android':
|
||||
$platform = 4;
|
||||
break;
|
||||
|
||||
case NULL:
|
||||
$platform = 7;
|
||||
break;
|
||||
|
||||
default:
|
||||
$platform = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
$response[$i]->last_seen = (object) [
|
||||
"platform" => 1,
|
||||
"platform" => $platform,
|
||||
"time" => $usr->getOnline()->timestamp()
|
||||
];
|
||||
}
|
||||
case "music":
|
||||
$response[$i]->music = $usr->getFavoriteMusic();
|
||||
break;
|
||||
|
@ -130,7 +158,10 @@ final class Users extends VKAPIRequestHandler
|
|||
break;
|
||||
case "interests":
|
||||
$response[$i]->interests = $usr->getInterests();
|
||||
break;
|
||||
break;
|
||||
case "rating":
|
||||
$response[$i]->rating = $usr->getRating();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -168,19 +199,94 @@ final class Users extends VKAPIRequestHandler
|
|||
];
|
||||
}
|
||||
|
||||
function search(string $q, string $fields = "", int $offset = 0, int $count = 100)
|
||||
function search(string $q,
|
||||
string $fields = "",
|
||||
int $offset = 0,
|
||||
int $count = 100,
|
||||
string $city = "",
|
||||
string $hometown = "",
|
||||
int $sex = 2,
|
||||
int $status = 0, # это про marital status
|
||||
bool $online = false,
|
||||
# дальше идут параметры которых нету в vkapi но есть на сайте
|
||||
string $profileStatus = "", # а это уже нормальный статус
|
||||
int $sort = 0,
|
||||
int $before = 0,
|
||||
int $politViews = 0,
|
||||
int $after = 0,
|
||||
string $interests = "",
|
||||
string $fav_music = "",
|
||||
string $fav_films = "",
|
||||
string $fav_shows = "",
|
||||
string $fav_books = "",
|
||||
string $fav_quotes = ""
|
||||
)
|
||||
{
|
||||
$users = new UsersRepo;
|
||||
|
||||
$sortg = "id ASC";
|
||||
|
||||
$nfilds = $fields;
|
||||
|
||||
switch($sort) {
|
||||
case 0:
|
||||
$sortg = "id DESC";
|
||||
break;
|
||||
case 1:
|
||||
$sortg = "id ASC";
|
||||
break;
|
||||
case 2:
|
||||
$sortg = "first_name DESC";
|
||||
break;
|
||||
case 3:
|
||||
$sortg = "first_name ASC";
|
||||
break;
|
||||
case 4:
|
||||
$sortg = "rating DESC";
|
||||
|
||||
if(!str_contains($nfilds, "rating")) {
|
||||
$nfilds .= "rating";
|
||||
}
|
||||
|
||||
break;
|
||||
case 5:
|
||||
$sortg = "rating DESC";
|
||||
|
||||
if(!str_contains($nfilds, "rating")) {
|
||||
$nfilds .= "rating";
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
$array = [];
|
||||
$find = $users->find($q);
|
||||
|
||||
$parameters = [
|
||||
"city" => !empty($city) ? $city : NULL,
|
||||
"hometown" => !empty($hometown) ? $hometown : NULL,
|
||||
"gender" => $sex < 2 ? $sex : NULL,
|
||||
"maritalstatus" => (bool)$status ? $status : NULL,
|
||||
"politViews" => (bool)$politViews ? $politViews : NULL,
|
||||
"is_online" => $online ? 1 : NULL,
|
||||
"status" => !empty($profileStatus) ? $profileStatus : NULL,
|
||||
"before" => $before != 0 ? $before : NULL,
|
||||
"after" => $after != 0 ? $after : NULL,
|
||||
"interests" => !empty($interests) ? $interests : NULL,
|
||||
"fav_music" => !empty($fav_music) ? $fav_music : NULL,
|
||||
"fav_films" => !empty($fav_films) ? $fav_films : NULL,
|
||||
"fav_shows" => !empty($fav_shows) ? $fav_shows : NULL,
|
||||
"fav_books" => !empty($fav_books) ? $fav_books : NULL,
|
||||
"fav_quotes" => !empty($fav_quotes) ? $fav_quotes : NULL,
|
||||
];
|
||||
|
||||
$find = $users->find($q, $parameters, $sortg);
|
||||
|
||||
foreach ($find as $user)
|
||||
$array[] = $user->getId();
|
||||
|
||||
return (object) [
|
||||
"count" => $find->size(),
|
||||
"items" => $this->get(implode(',', $array), $fields, $offset, $count)
|
||||
"items" => $this->get(implode(',', $array), $nfilds, $offset, $count)
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
<?php declare(strict_types=1);
|
||||
namespace openvk\VKAPI\Handlers;
|
||||
use openvk\Web\Models\Repositories\{Users, Clubs};
|
||||
|
||||
final class Utils extends VKAPIRequestHandler
|
||||
{
|
||||
|
@ -7,4 +8,39 @@ final class Utils extends VKAPIRequestHandler
|
|||
{
|
||||
return time();
|
||||
}
|
||||
|
||||
function resolveScreenName(string $screen_name): object
|
||||
{
|
||||
if(\Chandler\MVC\Routing\Router::i()->getMatchingRoute("/$screen_name")[0]->presenter !== "UnknownTextRouteStrategy") {
|
||||
if(substr($screen_name, 0, strlen("id")) === "id") {
|
||||
return (object) [
|
||||
"object_id" => (int) substr($screen_name, strlen("id")),
|
||||
"type" => "user"
|
||||
];
|
||||
} else if(substr($screen_name, 0, strlen("club")) === "club") {
|
||||
return (object) [
|
||||
"object_id" => (int) substr($screen_name, strlen("club")),
|
||||
"type" => "group"
|
||||
];
|
||||
}
|
||||
} else {
|
||||
$user = (new Users)->getByShortURL($screen_name);
|
||||
if($user) {
|
||||
return (object) [
|
||||
"object_id" => $user->getId(),
|
||||
"type" => "user"
|
||||
];
|
||||
}
|
||||
|
||||
$club = (new Clubs)->getByShortURL($screen_name);
|
||||
if($club) {
|
||||
return (object) [
|
||||
"object_id" => $club->getId(),
|
||||
"type" => "group"
|
||||
];
|
||||
}
|
||||
|
||||
return (object) [];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,15 +1,19 @@
|
|||
<?php declare(strict_types=1);
|
||||
namespace openvk\VKAPI\Handlers;
|
||||
use openvk\VKAPI\Exceptions\APIErrorException;
|
||||
use openvk\Web\Models\Entities\IP;
|
||||
use openvk\Web\Models\Entities\User;
|
||||
use openvk\Web\Models\Repositories\IPs;
|
||||
|
||||
abstract class VKAPIRequestHandler
|
||||
{
|
||||
protected $user;
|
||||
protected $platform;
|
||||
|
||||
function __construct(?User $user = NULL)
|
||||
function __construct(?User $user = NULL, ?string $platform = NULL)
|
||||
{
|
||||
$this->user = $user;
|
||||
$this->user = $user;
|
||||
$this->platform = $platform;
|
||||
}
|
||||
|
||||
protected function fail(int $code, string $message): void
|
||||
|
@ -22,6 +26,11 @@ abstract class VKAPIRequestHandler
|
|||
return $this->user;
|
||||
}
|
||||
|
||||
protected function getPlatform(): ?string
|
||||
{
|
||||
return $this->platform;
|
||||
}
|
||||
|
||||
protected function userAuthorized(): bool
|
||||
{
|
||||
return !is_null($this->getUser());
|
||||
|
@ -32,4 +41,19 @@ abstract class VKAPIRequestHandler
|
|||
if(!$this->userAuthorized())
|
||||
$this->fail(5, "User authorization failed: no access_token passed.");
|
||||
}
|
||||
|
||||
protected function willExecuteWriteAction(): void
|
||||
{
|
||||
$ip = (new IPs)->get(CONNECTING_IP);
|
||||
$res = $ip->rateLimit();
|
||||
|
||||
if(!($res === IP::RL_RESET || $res === IP::RL_CANEXEC)) {
|
||||
if($res === IP::RL_BANNED && OPENVK_ROOT_CONF["openvk"]["preferences"]["security"]["rateLimits"]["autoban"]) {
|
||||
$this->user->ban("User account has been suspended for breaking API terms of service", false);
|
||||
$this->fail(18, "User account has been suspended due to repeated violation of API rate limits.");
|
||||
}
|
||||
|
||||
$this->fail(29, "You have been rate limited.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
57
VKAPI/Handlers/Video.php
Executable file
57
VKAPI/Handlers/Video.php
Executable file
|
@ -0,0 +1,57 @@
|
|||
<?php declare(strict_types=1);
|
||||
namespace openvk\VKAPI\Handlers;
|
||||
use openvk\Web\Models\Entities\User;
|
||||
use openvk\Web\Models\Repositories\Users as UsersRepo;
|
||||
use openvk\Web\Models\Entities\Club;
|
||||
use openvk\Web\Models\Repositories\Clubs as ClubsRepo;
|
||||
use openvk\Web\Models\Entities\Video as VideoEntity;
|
||||
use openvk\Web\Models\Repositories\Videos as VideosRepo;
|
||||
use openvk\Web\Models\Entities\Comment;
|
||||
use openvk\Web\Models\Repositories\Comments as CommentsRepo;
|
||||
|
||||
final class Video extends VKAPIRequestHandler
|
||||
{
|
||||
function get(int $owner_id, string $videos, int $offset = 0, int $count = 30, int $extended = 0): object
|
||||
{
|
||||
$this->requireUser();
|
||||
|
||||
if ($videos) {
|
||||
$vids = explode(',', $videos);
|
||||
|
||||
foreach($vids as $vid)
|
||||
{
|
||||
$id = explode("_", $vid);
|
||||
|
||||
$items = [];
|
||||
|
||||
$video = (new VideosRepo)->getByOwnerAndVID(intval($id[0]), intval($id[1]));
|
||||
if($video) {
|
||||
$items[] = $video->getApiStructure();
|
||||
}
|
||||
}
|
||||
|
||||
return (object) [
|
||||
"count" => count($items),
|
||||
"items" => $items
|
||||
];
|
||||
} else {
|
||||
if ($owner_id > 0)
|
||||
$user = (new UsersRepo)->get($owner_id);
|
||||
else
|
||||
$this->fail(1, "Not implemented");
|
||||
|
||||
$videos = (new VideosRepo)->getByUser($user, $offset + 1, $count);
|
||||
$videosCount = (new VideosRepo)->getUserVideosCount($user);
|
||||
|
||||
$items = [];
|
||||
foreach ($videos as $video) {
|
||||
$items[] = $video->getApiStructure();
|
||||
}
|
||||
|
||||
return (object) [
|
||||
"count" => $videosCount,
|
||||
"items" => $items
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
|
@ -9,11 +9,19 @@ use openvk\Web\Models\Entities\Post;
|
|||
use openvk\Web\Models\Repositories\Posts as PostsRepo;
|
||||
use openvk\Web\Models\Entities\Comment;
|
||||
use openvk\Web\Models\Repositories\Comments as CommentsRepo;
|
||||
use openvk\Web\Models\Entities\Photo;
|
||||
use openvk\Web\Models\Repositories\Photos as PhotosRepo;
|
||||
use openvk\Web\Models\Entities\Video;
|
||||
use openvk\Web\Models\Repositories\Videos as VideosRepo;
|
||||
use openvk\Web\Models\Entities\Note;
|
||||
use openvk\Web\Models\Repositories\Notes as NotesRepo;
|
||||
|
||||
final class Wall extends VKAPIRequestHandler
|
||||
{
|
||||
function get(int $owner_id, string $domain = "", int $offset = 0, int $count = 30, int $extended = 0): object
|
||||
{
|
||||
$this->requireUser();
|
||||
|
||||
$posts = new PostsRepo;
|
||||
|
||||
$items = [];
|
||||
|
@ -42,10 +50,14 @@ final class Wall extends VKAPIRequestHandler
|
|||
if($attachment instanceof \openvk\Web\Models\Entities\Photo) {
|
||||
if($attachment->isDeleted())
|
||||
continue;
|
||||
|
||||
|
||||
$attachments[] = $this->getApiPhoto($attachment);
|
||||
} else if($attachment instanceof \openvk\Web\Models\Entities\Poll) {
|
||||
$attachments[] = $this->getApiPoll($attachment, $this->getUser());
|
||||
} else if ($attachment instanceof \openvk\Web\Models\Entities\Video) {
|
||||
$attachments[] = $attachment->getApiStructure();
|
||||
} else if ($attachment instanceof \openvk\Web\Models\Entities\Note) {
|
||||
$attachments[] = $attachment->toVkApiStruct();
|
||||
} else if ($attachment instanceof \openvk\Web\Models\Entities\Post) {
|
||||
$repostAttachments = [];
|
||||
|
||||
|
@ -53,7 +65,7 @@ final class Wall extends VKAPIRequestHandler
|
|||
if($repostAttachment instanceof \openvk\Web\Models\Entities\Photo) {
|
||||
if($repostAttachment->isDeleted())
|
||||
continue;
|
||||
|
||||
|
||||
$repostAttachments[] = $this->getApiPhoto($repostAttachment);
|
||||
/* Рекурсии, сука! Заказывали? */
|
||||
}
|
||||
|
@ -64,6 +76,17 @@ final class Wall extends VKAPIRequestHandler
|
|||
else
|
||||
$profiles[] = $attachment->getOwner()->getId();
|
||||
|
||||
$post_source = [];
|
||||
|
||||
if($attachment->getPlatform(true) === NULL) {
|
||||
$post_source = (object)["type" => "vk"];
|
||||
} else {
|
||||
$post_source = (object)[
|
||||
"type" => "api",
|
||||
"platform" => $attachment->getPlatform(true)
|
||||
];
|
||||
}
|
||||
|
||||
$repost[] = [
|
||||
"id" => $attachment->getVirtualId(),
|
||||
"owner_id" => $attachment->isPostedOnBehalfOfGroup() ? $attachment->getOwner()->getId() * -1 : $attachment->getOwner()->getId(),
|
||||
|
@ -72,13 +95,22 @@ final class Wall extends VKAPIRequestHandler
|
|||
"post_type" => "post",
|
||||
"text" => $attachment->getText(false),
|
||||
"attachments" => $repostAttachments,
|
||||
"post_source" => [
|
||||
"type" => "vk"
|
||||
],
|
||||
"post_source" => $post_source,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
$post_source = [];
|
||||
|
||||
if($post->getPlatform(true) === NULL) {
|
||||
$post_source = (object)["type" => "vk"];
|
||||
} else {
|
||||
$post_source = (object)[
|
||||
"type" => "api",
|
||||
"platform" => $post->getPlatform(true)
|
||||
];
|
||||
}
|
||||
|
||||
$items[] = (object)[
|
||||
"id" => $post->getVirtualId(),
|
||||
"from_id" => $from_id,
|
||||
|
@ -93,8 +125,9 @@ final class Wall extends VKAPIRequestHandler
|
|||
"can_archive" => false, # TODO MAYBE
|
||||
"is_archived" => false,
|
||||
"is_pinned" => $post->isPinned(),
|
||||
"is_explicit" => $post->isExplicit(),
|
||||
"attachments" => $attachments,
|
||||
"post_source" => (object)["type" => "vk"],
|
||||
"post_source" => $post_source,
|
||||
"comments" => (object)[
|
||||
"count" => $post->getCommentsCount(),
|
||||
"can_post" => 1
|
||||
|
@ -110,7 +143,7 @@ final class Wall extends VKAPIRequestHandler
|
|||
"user_reposted" => 0
|
||||
]
|
||||
];
|
||||
|
||||
|
||||
if ($from_id > 0)
|
||||
$profiles[] = $from_id;
|
||||
else
|
||||
|
@ -138,7 +171,8 @@ final class Wall extends VKAPIRequestHandler
|
|||
"screen_name" => $user->getShortCode(),
|
||||
"photo_50" => $user->getAvatarUrl(),
|
||||
"photo_100" => $user->getAvatarUrl(),
|
||||
"online" => $user->isOnline()
|
||||
"online" => $user->isOnline(),
|
||||
"verified" => $user->isVerified()
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -153,6 +187,7 @@ final class Wall extends VKAPIRequestHandler
|
|||
"photo_50" => $group->getAvatarUrl(),
|
||||
"photo_100" => $group->getAvatarUrl(),
|
||||
"photo_200" => $group->getAvatarUrl(),
|
||||
"verified" => $group->isVerified()
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -185,7 +220,7 @@ final class Wall extends VKAPIRequestHandler
|
|||
foreach($psts as $pst) {
|
||||
$id = explode("_", $pst);
|
||||
$post = (new PostsRepo)->getPostById(intval($id[0]), intval($id[1]));
|
||||
if($post) {
|
||||
if($post && !$post->isDeleted()) {
|
||||
$from_id = get_class($post->getOwner()) == "openvk\Web\Models\Entities\Club" ? $post->getOwner()->getId() * (-1) : $post->getOwner()->getId();
|
||||
$attachments = [];
|
||||
$repost = []; // чел высрал семь сигарет 😳 помянем 🕯
|
||||
|
@ -194,6 +229,10 @@ final class Wall extends VKAPIRequestHandler
|
|||
$attachments[] = $this->getApiPhoto($attachment);
|
||||
} else if($attachment instanceof \openvk\Web\Models\Entities\Poll) {
|
||||
$attachments[] = $this->getApiPoll($attachment, $user);
|
||||
} else if ($attachment instanceof \openvk\Web\Models\Entities\Video) {
|
||||
$attachments[] = $attachment->getApiStructure();
|
||||
} else if ($attachment instanceof \openvk\Web\Models\Entities\Note) {
|
||||
$attachments[] = $attachment->toVkApiStruct();
|
||||
} else if ($attachment instanceof \openvk\Web\Models\Entities\Post) {
|
||||
$repostAttachments = [];
|
||||
|
||||
|
@ -201,17 +240,28 @@ final class Wall extends VKAPIRequestHandler
|
|||
if($repostAttachment instanceof \openvk\Web\Models\Entities\Photo) {
|
||||
if($attachment->isDeleted())
|
||||
continue;
|
||||
|
||||
|
||||
$repostAttachments[] = $this->getApiPhoto($repostAttachment);
|
||||
/* Рекурсии, сука! Заказывали? */
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if ($attachment->isPostedOnBehalfOfGroup())
|
||||
$groups[] = $attachment->getOwner()->getId();
|
||||
else
|
||||
$profiles[] = $attachment->getOwner()->getId();
|
||||
|
||||
$post_source = [];
|
||||
|
||||
if($attachment->getPlatform(true) === NULL) {
|
||||
$post_source = (object)["type" => "vk"];
|
||||
} else {
|
||||
$post_source = (object)[
|
||||
"type" => "api",
|
||||
"platform" => $attachment->getPlatform(true)
|
||||
];
|
||||
}
|
||||
|
||||
$repost[] = [
|
||||
"id" => $attachment->getVirtualId(),
|
||||
"owner_id" => $attachment->isPostedOnBehalfOfGroup() ? $attachment->getOwner()->getId() * -1 : $attachment->getOwner()->getId(),
|
||||
|
@ -220,13 +270,22 @@ final class Wall extends VKAPIRequestHandler
|
|||
"post_type" => "post",
|
||||
"text" => $attachment->getText(false),
|
||||
"attachments" => $repostAttachments,
|
||||
"post_source" => [
|
||||
"type" => "vk"
|
||||
],
|
||||
"post_source" => $post_source,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
$post_source = [];
|
||||
|
||||
if($post->getPlatform(true) === NULL) {
|
||||
$post_source = (object)["type" => "vk"];
|
||||
} else {
|
||||
$post_source = (object)[
|
||||
"type" => "api",
|
||||
"platform" => $post->getPlatform(true)
|
||||
];
|
||||
}
|
||||
|
||||
$items[] = (object)[
|
||||
"id" => $post->getVirtualId(),
|
||||
"from_id" => $from_id,
|
||||
|
@ -241,7 +300,8 @@ final class Wall extends VKAPIRequestHandler
|
|||
"can_archive" => false, # TODO MAYBE
|
||||
"is_archived" => false,
|
||||
"is_pinned" => $post->isPinned(),
|
||||
"post_source" => (object)["type" => "vk"],
|
||||
"is_explicit" => $post->isExplicit(),
|
||||
"post_source" => $post_source,
|
||||
"attachments" => $attachments,
|
||||
"comments" => (object)[
|
||||
"count" => $post->getCommentsCount(),
|
||||
|
@ -258,7 +318,7 @@ final class Wall extends VKAPIRequestHandler
|
|||
"user_reposted" => 0
|
||||
]
|
||||
];
|
||||
|
||||
|
||||
if ($from_id > 0)
|
||||
$profiles[] = $from_id;
|
||||
else
|
||||
|
@ -288,7 +348,8 @@ final class Wall extends VKAPIRequestHandler
|
|||
"screen_name" => $user->getShortCode(),
|
||||
"photo_50" => $user->getAvatarUrl(),
|
||||
"photo_100" => $user->getAvatarUrl(),
|
||||
"online" => $user->isOnline()
|
||||
"online" => $user->isOnline(),
|
||||
"verified" => $user->isVerified()
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -303,6 +364,7 @@ final class Wall extends VKAPIRequestHandler
|
|||
"photo_50" => $group->getAvatarUrl(),
|
||||
"photo_100" => $group->getAvatarUrl(),
|
||||
"photo_200" => $group->getAvatarUrl(),
|
||||
"verified" => $group->isVerified()
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -317,12 +379,13 @@ final class Wall extends VKAPIRequestHandler
|
|||
];
|
||||
}
|
||||
|
||||
function post(string $owner_id, string $message = "", int $from_group = 0, int $signed = 0): object
|
||||
function post(string $owner_id, string $message = "", int $from_group = 0, int $signed = 0, string $attachments = ""): object
|
||||
{
|
||||
$this->requireUser();
|
||||
$this->willExecuteWriteAction();
|
||||
|
||||
$owner_id = intval($owner_id);
|
||||
|
||||
|
||||
$wallOwner = ($owner_id > 0 ? (new UsersRepo)->get($owner_id) : (new ClubsRepo)->get($owner_id * -1))
|
||||
?? $this->fail(18, "User was deleted or banned");
|
||||
if($owner_id > 0)
|
||||
|
@ -333,7 +396,7 @@ final class Wall extends VKAPIRequestHandler
|
|||
else
|
||||
$canPost = $wallOwner->canPost();
|
||||
else
|
||||
$canPost = false;
|
||||
$canPost = false;
|
||||
|
||||
if($canPost == false) $this->fail(15, "Access denied");
|
||||
|
||||
|
@ -354,27 +417,7 @@ final class Wall extends VKAPIRequestHandler
|
|||
if($signed == 1)
|
||||
$flags |= 0b01000000;
|
||||
|
||||
# TODO: Compatible implementation of this
|
||||
try {
|
||||
$photo = NULL;
|
||||
$video = NULL;
|
||||
if($_FILES["photo"]["error"] === UPLOAD_ERR_OK) {
|
||||
$album = NULL;
|
||||
if(!$anon && $owner_id > 0 && $owner_id === $this->getUser()->getId())
|
||||
$album = (new AlbumsRepo)->getUserWallAlbum($wallOwner);
|
||||
|
||||
$photo = Photo::fastMake($this->getUser()->getId(), $message, $_FILES["photo"], $album, $anon);
|
||||
}
|
||||
|
||||
if($_FILES["video"]["error"] === UPLOAD_ERR_OK)
|
||||
$video = Video::fastMake($this->getUser()->getId(), $message, $_FILES["video"], $anon);
|
||||
} catch(\DomainException $ex) {
|
||||
$this->fail(-156, "The media file is corrupted");
|
||||
} catch(ISE $ex) {
|
||||
$this->fail(-156, "The media file is corrupted or too large ");
|
||||
}
|
||||
|
||||
if(empty($message) && !$photo && !$video)
|
||||
if(empty($message) && empty($attachments))
|
||||
$this->fail(100, "Required parameter 'message' missing.");
|
||||
|
||||
try {
|
||||
|
@ -384,16 +427,66 @@ final class Wall extends VKAPIRequestHandler
|
|||
$post->setCreated(time());
|
||||
$post->setContent($message);
|
||||
$post->setFlags($flags);
|
||||
$post->setApi_Source_Name($this->getPlatform());
|
||||
$post->save();
|
||||
} catch(\LogicException $ex) {
|
||||
$this->fail(100, "One of the parameters specified was missing or invalid");
|
||||
}
|
||||
|
||||
if(!is_null($photo))
|
||||
$post->attach($photo);
|
||||
if(!empty($attachments)) {
|
||||
$attachmentsArr = explode(",", $attachments);
|
||||
# Аттачи такого вида: [тип][id владельца]_[id вложения]
|
||||
# Пример: photo1_1
|
||||
|
||||
if(!is_null($video))
|
||||
$post->attach($video);
|
||||
if(sizeof($attachmentsArr) > 10)
|
||||
$this->fail(50, "Error: too many attachments");
|
||||
|
||||
foreach($attachmentsArr as $attac) {
|
||||
$attachmentType = NULL;
|
||||
|
||||
if(str_contains($attac, "photo"))
|
||||
$attachmentType = "photo";
|
||||
elseif(str_contains($attac, "video"))
|
||||
$attachmentType = "video";
|
||||
elseif(str_contains($attac, "note"))
|
||||
$attachmentType = "note";
|
||||
else
|
||||
$this->fail(205, "Unknown attachment type");
|
||||
|
||||
$attachment = str_replace($attachmentType, "", $attac);
|
||||
|
||||
$attachmentOwner = (int)explode("_", $attachment)[0];
|
||||
$attachmentId = (int)end(explode("_", $attachment));
|
||||
|
||||
$attacc = NULL;
|
||||
|
||||
if($attachmentType == "photo") {
|
||||
$attacc = (new PhotosRepo)->getByOwnerAndVID($attachmentOwner, $attachmentId);
|
||||
if(!$attacc || $attacc->isDeleted())
|
||||
$this->fail(100, "Invalid photo");
|
||||
if(!$attacc->getOwner()->getPrivacyPermission('photos.read', $this->getUser()))
|
||||
$this->fail(43, "Access to photo denied");
|
||||
|
||||
$post->attach($attacc);
|
||||
} elseif($attachmentType == "video") {
|
||||
$attacc = (new VideosRepo)->getByOwnerAndVID($attachmentOwner, $attachmentId);
|
||||
if(!$attacc || $attacc->isDeleted())
|
||||
$this->fail(100, "Video does not exists");
|
||||
if(!$attacc->getOwner()->getPrivacyPermission('videos.read', $this->getUser()))
|
||||
$this->fail(43, "Access to video denied");
|
||||
|
||||
$post->attach($attacc);
|
||||
} elseif($attachmentType == "note") {
|
||||
$attacc = (new NotesRepo)->getNoteById($attachmentOwner, $attachmentId);
|
||||
if(!$attacc || $attacc->isDeleted())
|
||||
$this->fail(100, "Note does not exist");
|
||||
if(!$attacc->getOwner()->getPrivacyPermission('notes.read', $this->getUser()))
|
||||
$this->fail(11, "Access to note denied");
|
||||
|
||||
$post->attach($attacc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if($wall > 0 && $wall !== $this->user->identity->getId())
|
||||
(new WallPostNotification($wallOwner, $post, $this->user->identity))->emit();
|
||||
|
@ -401,8 +494,9 @@ final class Wall extends VKAPIRequestHandler
|
|||
return (object)["post_id" => $post->getVirtualId()];
|
||||
}
|
||||
|
||||
function repost(string $object, string $message = "") {
|
||||
function repost(string $object, string $message = "", int $group_id = 0) {
|
||||
$this->requireUser();
|
||||
$this->willExecuteWriteAction();
|
||||
|
||||
$postArray;
|
||||
if(preg_match('/wall((?:-?)[0-9]+)_([0-9]+)/', $object, $postArray) == 0)
|
||||
|
@ -413,13 +507,27 @@ final class Wall extends VKAPIRequestHandler
|
|||
|
||||
$nPost = new Post;
|
||||
$nPost->setOwner($this->user->getId());
|
||||
$nPost->setWall($this->user->getId());
|
||||
|
||||
if($group_id > 0) {
|
||||
$club = (new ClubsRepo)->get($group_id);
|
||||
if(!$club)
|
||||
$this->fail(42, "Invalid group");
|
||||
|
||||
if(!$club->canBeModifiedBy($this->user))
|
||||
$this->fail(16, "Access to group denied");
|
||||
|
||||
$nPost->setWall($group_id * -1);
|
||||
} else {
|
||||
$nPost->setWall($this->user->getId());
|
||||
}
|
||||
|
||||
$nPost->setContent($message);
|
||||
$nPost->setApi_Source_Name($this->getPlatform());
|
||||
$nPost->save();
|
||||
$nPost->attach($post);
|
||||
|
||||
if($post->getOwner(false)->getId() !== $this->user->getId() && !($post->getOwner() instanceof Club))
|
||||
(new RepostNotification($post->getOwner(false), $post, $this->user->identity))->emit();
|
||||
(new RepostNotification($post->getOwner(false), $post, $this->user))->emit();
|
||||
|
||||
return (object) [
|
||||
"success" => 1, // 👍
|
||||
|
@ -429,6 +537,7 @@ final class Wall extends VKAPIRequestHandler
|
|||
];
|
||||
}
|
||||
|
||||
|
||||
function getComments(int $owner_id, int $post_id, bool $need_likes = true, int $offset = 0, int $count = 10, string $fields = "sex,screen_name,photo_50,photo_100,online_info,online", string $sort = "asc", bool $extended = false) {
|
||||
$this->requireUser();
|
||||
|
||||
|
@ -436,7 +545,7 @@ final class Wall extends VKAPIRequestHandler
|
|||
if(!$post || $post->isDeleted()) $this->fail(100, "One of the parameters specified was missing or invalid");
|
||||
|
||||
$comments = (new CommentsRepo)->getCommentsByTarget($post, $offset+1, $count, $sort == "desc" ? "DESC" : "ASC");
|
||||
|
||||
|
||||
$items = [];
|
||||
$profiles = [];
|
||||
|
||||
|
@ -445,7 +554,17 @@ final class Wall extends VKAPIRequestHandler
|
|||
$oid = $owner->getId();
|
||||
if($owner instanceof Club)
|
||||
$oid *= -1;
|
||||
|
||||
|
||||
$attachments = [];
|
||||
|
||||
foreach($comment->getChildren() as $attachment) {
|
||||
if($attachment instanceof \openvk\Web\Models\Entities\Photo) {
|
||||
$attachments[] = $this->getApiPhoto($attachment);
|
||||
} elseif($attachment instanceof \openvk\Web\Models\Entities\Note) {
|
||||
$attachments[] = $attachment->toVkApiStruct();
|
||||
}
|
||||
}
|
||||
|
||||
$item = [
|
||||
"id" => $comment->getId(),
|
||||
"from_id" => $oid,
|
||||
|
@ -454,6 +573,7 @@ final class Wall extends VKAPIRequestHandler
|
|||
"post_id" => $post->getVirtualId(),
|
||||
"owner_id" => $post->isPostedOnBehalfOfGroup() ? $post->getOwner()->getId() * -1 : $post->getOwner()->getId(),
|
||||
"parents_stack" => [],
|
||||
"attachments" => $attachments,
|
||||
"thread" => [
|
||||
"count" => 0,
|
||||
"items" => [],
|
||||
|
@ -470,10 +590,13 @@ final class Wall extends VKAPIRequestHandler
|
|||
"user_likes" => (int) $comment->hasLikeFrom($this->getUser()),
|
||||
"can_publish" => 1
|
||||
];
|
||||
|
||||
|
||||
$items[] = $item;
|
||||
if($extended == true)
|
||||
$profiles[] = $comment->getOwner()->getId();
|
||||
|
||||
$attachments = null;
|
||||
// Reset $attachments to not duplicate prikols
|
||||
}
|
||||
|
||||
$response = [
|
||||
|
@ -496,10 +619,18 @@ final class Wall extends VKAPIRequestHandler
|
|||
function getComment(int $owner_id, int $comment_id, bool $extended = false, string $fields = "sex,screen_name,photo_50,photo_100,online_info,online") {
|
||||
$this->requireUser();
|
||||
|
||||
$comment = (new CommentsRepo)->get($comment_id); // один хуй айди всех комментов общий
|
||||
|
||||
$comment = (new CommentsRepo)->get($comment_id); # один хуй айди всех комментов общий
|
||||
|
||||
$profiles = [];
|
||||
|
||||
$attachments = [];
|
||||
|
||||
foreach($comment->getChildren() as $attachment) {
|
||||
if($attachment instanceof \openvk\Web\Models\Entities\Photo) {
|
||||
$attachments[] = $this->getApiPhoto($attachment);
|
||||
}
|
||||
}
|
||||
|
||||
$item = [
|
||||
"id" => $comment->getId(),
|
||||
"from_id" => $comment->getOwner()->getId(),
|
||||
|
@ -508,6 +639,7 @@ final class Wall extends VKAPIRequestHandler
|
|||
"post_id" => $comment->getTarget()->getVirtualId(),
|
||||
"owner_id" => $comment->getTarget()->isPostedOnBehalfOfGroup() ? $comment->getTarget()->getOwner()->getId() * -1 : $comment->getTarget()->getOwner()->getId(),
|
||||
"parents_stack" => [],
|
||||
"attachments" => $attachments,
|
||||
"likes" => [
|
||||
"can_like" => 1,
|
||||
"count" => $comment->getLikesCount(),
|
||||
|
@ -522,7 +654,7 @@ final class Wall extends VKAPIRequestHandler
|
|||
"groups_can_post" => false,
|
||||
]
|
||||
];
|
||||
|
||||
|
||||
if($extended == true)
|
||||
$profiles[] = $comment->getOwner()->getId();
|
||||
|
||||
|
@ -538,20 +670,29 @@ final class Wall extends VKAPIRequestHandler
|
|||
$response['profiles'] = (!empty($profiles) ? (new Users)->get(implode(',', $profiles), $fields) : []);
|
||||
}
|
||||
|
||||
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
function createComment(int $owner_id, int $post_id, string $message, int $from_group = 0) {
|
||||
function createComment(int $owner_id, int $post_id, string $message = "", int $from_group = 0, string $attachments = "") {
|
||||
$this->requireUser();
|
||||
$this->willExecuteWriteAction();
|
||||
|
||||
$post = (new PostsRepo)->getPostById($owner_id, $post_id);
|
||||
if(!$post || $post->isDeleted()) $this->fail(100, "One of the parameters specified was missing or invalid");
|
||||
if(!$post || $post->isDeleted()) $this->fail(100, "Invalid post");
|
||||
|
||||
if($post->getTargetWall() < 0)
|
||||
$club = (new ClubsRepo)->get(abs($post->getTargetWall()));
|
||||
|
||||
|
||||
if(empty($message) && empty($attachments)) {
|
||||
$this->fail(100, "Required parameter 'message' missing.");
|
||||
}
|
||||
|
||||
$flags = 0;
|
||||
if($from_group != 0 && !is_null($club) && $club->canBeModifiedBy($this->user))
|
||||
$flags |= 0b10000000;
|
||||
|
||||
|
||||
try {
|
||||
$comment = new Comment;
|
||||
$comment->setOwner($this->user->getId());
|
||||
|
@ -564,11 +705,54 @@ final class Wall extends VKAPIRequestHandler
|
|||
} catch (\LengthException $ex) {
|
||||
$this->fail(1, "ошибка про то что коммент большой слишком");
|
||||
}
|
||||
|
||||
|
||||
if(!empty($attachments)) {
|
||||
$attachmentsArr = explode(",", $attachments);
|
||||
|
||||
if(sizeof($attachmentsArr) > 10)
|
||||
$this->fail(50, "Error: too many attachments");
|
||||
|
||||
foreach($attachmentsArr as $attac) {
|
||||
$attachmentType = NULL;
|
||||
|
||||
if(str_contains($attac, "photo"))
|
||||
$attachmentType = "photo";
|
||||
elseif(str_contains($attac, "video"))
|
||||
$attachmentType = "video";
|
||||
else
|
||||
$this->fail(205, "Unknown attachment type");
|
||||
|
||||
$attachment = str_replace($attachmentType, "", $attac);
|
||||
|
||||
$attachmentOwner = (int)explode("_", $attachment)[0];
|
||||
$attachmentId = (int)end(explode("_", $attachment));
|
||||
|
||||
$attacc = NULL;
|
||||
|
||||
if($attachmentType == "photo") {
|
||||
$attacc = (new PhotosRepo)->getByOwnerAndVID($attachmentOwner, $attachmentId);
|
||||
if(!$attacc || $attacc->isDeleted())
|
||||
$this->fail(100, "Photo does not exists");
|
||||
if(!$attacc->getOwner()->getPrivacyPermission('photos.read', $this->getUser()))
|
||||
$this->fail(11, "Access to photo denied");
|
||||
|
||||
$comment->attach($attacc);
|
||||
} elseif($attachmentType == "video") {
|
||||
$attacc = (new VideosRepo)->getByOwnerAndVID($attachmentOwner, $attachmentId);
|
||||
if(!$attacc || $attacc->isDeleted())
|
||||
$this->fail(100, "Video does not exists");
|
||||
if(!$attacc->getOwner()->getPrivacyPermission('videos.read', $this->getUser()))
|
||||
$this->fail(11, "Access to video denied");
|
||||
|
||||
$comment->attach($attacc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if($post->getOwner()->getId() !== $this->user->getId())
|
||||
if(($owner = $post->getOwner()) instanceof User)
|
||||
(new CommentNotification($owner, $comment, $post, $this->user))->emit();
|
||||
|
||||
|
||||
return (object) [
|
||||
"comment_id" => $comment->getId(),
|
||||
"parents_stack" => []
|
||||
|
@ -577,17 +761,18 @@ final class Wall extends VKAPIRequestHandler
|
|||
|
||||
function deleteComment(int $comment_id) {
|
||||
$this->requireUser();
|
||||
$this->willExecuteWriteAction();
|
||||
|
||||
$comment = (new CommentsRepo)->get($comment_id);
|
||||
if(!$comment) $this->fail(100, "One of the parameters specified was missing or invalid");;
|
||||
if(!$comment->canBeDeletedBy($this->user))
|
||||
$this->fail(7, "Access denied");
|
||||
|
||||
|
||||
$comment->delete();
|
||||
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
private function getApiPhoto($attachment) {
|
||||
return [
|
||||
"type" => "photo",
|
||||
|
@ -596,7 +781,7 @@ final class Wall extends VKAPIRequestHandler
|
|||
"date" => $attachment->getPublicationTime()->timestamp(),
|
||||
"id" => $attachment->getVirtualId(),
|
||||
"owner_id" => $attachment->getOwner()->getId(),
|
||||
"sizes" => array_values($attachment->getVkApiSizes()),
|
||||
"sizes" => !is_null($attachment->getVkApiSizes()) ? array_values($attachment->getVkApiSizes()) : NULL,
|
||||
"text" => "",
|
||||
"has_tags" => false
|
||||
]
|
||||
|
@ -613,7 +798,7 @@ final class Wall extends VKAPIRequestHandler
|
|||
"votes" => $answer->votes
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
$userVote = array();
|
||||
foreach($attachment->getUserVote($user) as $vote)
|
||||
$userVote[] = $vote[0];
|
||||
|
|
|
@ -5,7 +5,7 @@ exceptions. It is still a work-in-progress functionality.
|
|||
**Note**: requests to API are routed through
|
||||
openvk.Web.Presenters.VKAPIPresenter, this dir contains only handlers.
|
||||
|
||||
[Documentation for API clients](https://docs.openvk.su/openvk_engine/api/description/)
|
||||
[Documentation for API clients](https://docs.openvk.uk/openvk_engine/api/description/)
|
||||
|
||||
## Implementing API methods
|
||||
|
||||
|
|
|
@ -22,6 +22,11 @@ class APIToken extends RowModel
|
|||
{
|
||||
return $this->getId() . "-" . chunk_split($this->getSecret(), 8, "-") . "jill";
|
||||
}
|
||||
|
||||
function getPlatform(): ?string
|
||||
{
|
||||
return $this->getRecord()->platform;
|
||||
}
|
||||
|
||||
function isRevoked(): bool
|
||||
{
|
||||
|
|
|
@ -66,4 +66,31 @@ class Album extends MediaCollection
|
|||
{
|
||||
return $this->has($photo);
|
||||
}
|
||||
|
||||
function toVkApiStruct(?User $user = NULL, bool $need_covers = false, bool $photo_sizes = false): object
|
||||
{
|
||||
$res = (object) [];
|
||||
|
||||
$res->id = $this->getPrettyId();
|
||||
$res->thumb_id = !is_null($this->getCoverPhoto()) ? $this->getCoverPhoto()->getPrettyId() : 0;
|
||||
$res->owner_id = $this->getOwner()->getId();
|
||||
$res->title = $this->getName();
|
||||
$res->description = $this->getDescription();
|
||||
$res->created = $this->getCreationTime()->timestamp();
|
||||
$res->updated = $this->getEditTime() ? $this->getEditTime()->timestamp() : NULL;
|
||||
$res->size = $this->size();
|
||||
$res->privacy_comment = 1;
|
||||
$res->upload_by_admins_only = 1;
|
||||
$res->comments_disabled = 0;
|
||||
$res->can_upload = $this->canBeModifiedBy($user); # thisUser недоступен в entities
|
||||
if($need_covers) {
|
||||
$res->thumb_src = $this->getCoverURL();
|
||||
|
||||
if($photo_sizes) {
|
||||
$res->sizes = !is_null($this->getCoverPhoto()) ? $this->getCoverPhoto()->getVkApiSizes() : NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return $res;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -306,11 +306,14 @@ class Application extends RowModel
|
|||
function delete(bool $softly = true): void
|
||||
{
|
||||
if($softly)
|
||||
throw new \UnexpectedValueException("Can't delete apps softly.");
|
||||
throw new \UnexpectedValueException("Can't delete apps softly."); // why
|
||||
|
||||
$cx = DatabaseConnection::i()->getContext();
|
||||
$cx->table("app_users")->where("app", $this->getId())->delete();
|
||||
|
||||
parent::delete(false);
|
||||
}
|
||||
|
||||
function getPublicationTime(): string
|
||||
{ return tr("recently"); }
|
||||
}
|
66
Web/Models/Entities/Ban.php
Normal file
66
Web/Models/Entities/Ban.php
Normal file
|
@ -0,0 +1,66 @@
|
|||
<?php declare(strict_types=1);
|
||||
namespace openvk\Web\Models\Entities;
|
||||
use openvk\Web\Models\RowModel;
|
||||
use openvk\Web\Util\DateTime;
|
||||
use openvk\Web\Models\Repositories\{Users};
|
||||
use Nette\Database\Table\ActiveRow;
|
||||
|
||||
class Ban extends RowModel
|
||||
{
|
||||
protected $tableName = "bans";
|
||||
|
||||
function getId(): int
|
||||
{
|
||||
return $this->getRecord()->id;
|
||||
}
|
||||
|
||||
function getReason(): ?string
|
||||
{
|
||||
return $this->getRecord()->reason;
|
||||
}
|
||||
|
||||
function getUser(): ?User
|
||||
{
|
||||
return (new Users)->get($this->getRecord()->user);
|
||||
}
|
||||
|
||||
function getInitiator(): ?User
|
||||
{
|
||||
return (new Users)->get($this->getRecord()->initiator);
|
||||
}
|
||||
|
||||
function getStartTime(): int
|
||||
{
|
||||
return $this->getRecord()->iat;
|
||||
}
|
||||
|
||||
function getEndTime(): int
|
||||
{
|
||||
return $this->getRecord()->exp;
|
||||
}
|
||||
|
||||
function getTime(): int
|
||||
{
|
||||
return $this->getRecord()->time;
|
||||
}
|
||||
|
||||
function isPermanent(): bool
|
||||
{
|
||||
return $this->getEndTime() === 0;
|
||||
}
|
||||
|
||||
function isRemovedManually(): bool
|
||||
{
|
||||
return (bool) $this->getRecord()->removed_manually;
|
||||
}
|
||||
|
||||
function isOver(): bool
|
||||
{
|
||||
return $this->isRemovedManually();
|
||||
}
|
||||
|
||||
function whoRemoved(): ?User
|
||||
{
|
||||
return (new Users)->get($this->getRecord()->removed_by);
|
||||
}
|
||||
}
|
|
@ -160,7 +160,7 @@ class Club extends RowModel
|
|||
|
||||
function canPost(): bool
|
||||
{
|
||||
return (bool) $this->getRecord()->wall;
|
||||
return (bool) $this->getRecord()->wall;
|
||||
}
|
||||
|
||||
|
||||
|
@ -224,7 +224,7 @@ class Club extends RowModel
|
|||
"shape" => "spline",
|
||||
"color" => "#597da3",
|
||||
],
|
||||
"name" => $unique ? "Полный охват" : "Все просмотры",
|
||||
"name" => $unique ? tr("full_coverage") : tr("all_views"),
|
||||
],
|
||||
"subs" => [
|
||||
"x" => array_reverse(range(1, 7)),
|
||||
|
@ -235,7 +235,7 @@ class Club extends RowModel
|
|||
"color" => "#b05c91",
|
||||
],
|
||||
"fill" => "tozeroy",
|
||||
"name" => $unique ? "Охват подписчиков" : "Просмотры подписчиков",
|
||||
"name" => $unique ? tr("subs_coverage") : tr("subs_views"),
|
||||
],
|
||||
"viral" => [
|
||||
"x" => array_reverse(range(1, 7)),
|
||||
|
@ -246,7 +246,7 @@ class Club extends RowModel
|
|||
"color" => "#4d9fab",
|
||||
],
|
||||
"fill" => "tozeroy",
|
||||
"name" => $unique ? "Виральный охват" : "Виральные просмотры",
|
||||
"name" => $unique ? tr("viral_coverage") : tr("viral_views"),
|
||||
],
|
||||
];
|
||||
}
|
||||
|
@ -262,17 +262,17 @@ class Club extends RowModel
|
|||
return $subbed && ($this->getOpennesStatus() === static::CLOSED ? $this->isSubscriptionAccepted($user) : true);
|
||||
}
|
||||
|
||||
function getFollowersQuery(): GroupedSelection
|
||||
function getFollowersQuery(string $sort = "follower ASC"): GroupedSelection
|
||||
{
|
||||
$query = $this->getRecord()->related("subscriptions.target");
|
||||
|
||||
if($this->getOpennesStatus() === static::OPEN) {
|
||||
$query = $query->where("model", "openvk\\Web\\Models\\Entities\\Club");
|
||||
$query = $query->where("model", "openvk\\Web\\Models\\Entities\\Club")->order($sort);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $query;
|
||||
return $query->group("follower");
|
||||
}
|
||||
|
||||
function getFollowersCount(): int
|
||||
|
@ -280,9 +280,9 @@ class Club extends RowModel
|
|||
return sizeof($this->getFollowersQuery());
|
||||
}
|
||||
|
||||
function getFollowers(int $page = 1): \Traversable
|
||||
function getFollowers(int $page = 1, int $perPage = 6, string $sort = "follower ASC"): \Traversable
|
||||
{
|
||||
$rels = $this->getFollowersQuery()->page($page, 6);
|
||||
$rels = $this->getFollowersQuery($sort)->page($page, $perPage);
|
||||
|
||||
foreach($rels as $rel) {
|
||||
$rel = (new Users)->get($rel->follower);
|
||||
|
@ -351,15 +351,56 @@ class Club extends RowModel
|
|||
}
|
||||
|
||||
function getWebsite(): ?string
|
||||
{
|
||||
return $this->getRecord()->website;
|
||||
}
|
||||
{
|
||||
return $this->getRecord()->website;
|
||||
}
|
||||
|
||||
function ban(string $reason): void
|
||||
{
|
||||
$this->setBlock_Reason($reason);
|
||||
$this->save();
|
||||
}
|
||||
|
||||
function unban(): void
|
||||
{
|
||||
$this->setBlock_Reason(null);
|
||||
$this->save();
|
||||
}
|
||||
|
||||
function getAlert(): ?string
|
||||
{
|
||||
return $this->getRecord()->alert;
|
||||
}
|
||||
|
||||
function toVkApiStruct(?User $user = NULL): object
|
||||
{
|
||||
$res = [];
|
||||
|
||||
$res->id = $this->getId();
|
||||
$res->name = $this->getName();
|
||||
$res->screen_name = $this->getShortCode();
|
||||
$res->is_closed = 0;
|
||||
$res->deactivated = NULL;
|
||||
$res->is_admin = $this->canBeModifiedBy($user);
|
||||
|
||||
if($this->canBeModifiedBy($user)) {
|
||||
$res->admin_level = 3;
|
||||
}
|
||||
|
||||
$res->is_member = $this->getSubscriptionStatus($user) ? 1 : 0;
|
||||
|
||||
$res->type = "group";
|
||||
$res->photo_50 = $this->getAvatarUrl("miniscule");
|
||||
$res->photo_100 = $this->getAvatarUrl("tiny");
|
||||
$res->photo_200 = $this->getAvatarUrl("normal");
|
||||
|
||||
$res->can_create_topic = $this->canBeModifiedBy($user) ? 1 : ($this->isEveryoneCanCreateTopics() ? 1 : 0);
|
||||
|
||||
$res->can_post = $this->canBeModifiedBy($user) ? 1 : ($this->canPost() ? 1 : 0);
|
||||
|
||||
return (object) $res;
|
||||
}
|
||||
|
||||
use Traits\TBackDrops;
|
||||
use Traits\TSubscribable;
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
namespace openvk\Web\Models\Entities;
|
||||
use openvk\Web\Models\Repositories\Clubs;
|
||||
use openvk\Web\Models\RowModel;
|
||||
use openvk\Web\Models\Entities\{Note};
|
||||
|
||||
class Comment extends Post
|
||||
{
|
||||
|
@ -10,7 +11,7 @@ class Comment extends Post
|
|||
|
||||
function getPrettyId(): string
|
||||
{
|
||||
return $this->getRecord()->id;
|
||||
return (string)$this->getRecord()->id;
|
||||
}
|
||||
|
||||
function getVirtualId(): int
|
||||
|
@ -52,4 +53,49 @@ class Comment extends Post
|
|||
$this->getTarget() instanceof Post && $this->getTarget()->getTargetWall() < 0 && (new Clubs)->get(abs($this->getTarget()->getTargetWall()))->canBeModifiedBy($user) ||
|
||||
$this->getTarget() instanceof Topic && $this->getTarget()->canBeModifiedBy($user);
|
||||
}
|
||||
|
||||
function toVkApiStruct(?User $user = NULL, bool $need_likes = false, bool $extended = false, ?Note $note = NULL): object
|
||||
{
|
||||
$res = (object) [];
|
||||
|
||||
$res->id = $this->getId();
|
||||
$res->from_id = $this->getOwner()->getId();
|
||||
$res->date = $this->getPublicationTime()->timestamp();
|
||||
$res->text = $this->getText(false);
|
||||
$res->attachments = [];
|
||||
$res->parents_stack = [];
|
||||
|
||||
if(!is_null($note)) {
|
||||
$res->uid = $this->getOwner()->getId();
|
||||
$res->nid = $note->getId();
|
||||
$res->oid = $note->getOwner()->getId();
|
||||
}
|
||||
|
||||
foreach($this->getChildren() as $attachment) {
|
||||
if($attachment->isDeleted())
|
||||
continue;
|
||||
|
||||
$res->attachments[] = $attachment->toVkApiStruct();
|
||||
}
|
||||
|
||||
if($need_likes) {
|
||||
$res->count = $this->getLikesCount();
|
||||
$res->user_likes = (int)$this->hasLikeFrom($user);
|
||||
$res->can_like = 1;
|
||||
}
|
||||
return $res;
|
||||
}
|
||||
|
||||
function getURL(): string
|
||||
{
|
||||
return "/wall" . $this->getTarget()->getPrettyId() . "#_comment" . $this->getId();
|
||||
}
|
||||
|
||||
function canBeEditedBy(?User $user = NULL): bool
|
||||
{
|
||||
if(!$user)
|
||||
return false;
|
||||
|
||||
return $user->getId() == $this->getOwner(false)->getId();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -92,7 +92,7 @@ class IP extends RowModel
|
|||
$this->stateChanges("rate_limit_counter", $aCounter);
|
||||
$this->stateChanges("rate_limit_violation_counter_start", $vCounterSessionStart);
|
||||
$this->stateChanges("rate_limit_violation_counter", $vCounter);
|
||||
$this->save();
|
||||
$this->save(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -105,11 +105,11 @@ class IP extends RowModel
|
|||
$this->stateChanges("ip", $ip);
|
||||
}
|
||||
|
||||
function save(): void
|
||||
function save(?bool $log = false): void
|
||||
{
|
||||
if(is_null($this->getRecord()))
|
||||
$this->stateChanges("first_seen", time());
|
||||
|
||||
parent::save();
|
||||
parent::save($log);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -121,14 +121,14 @@ abstract class Media extends Postable
|
|||
$this->stateChanges("hash", $hash);
|
||||
}
|
||||
|
||||
function save(): void
|
||||
function save(?bool $log = false): void
|
||||
{
|
||||
if(!is_null($this->processingPlaceholder) && is_null($this->getRecord())) {
|
||||
$this->stateChanges("processed", 0);
|
||||
$this->stateChanges("last_checked", time());
|
||||
}
|
||||
|
||||
parent::save();
|
||||
parent::save($log);
|
||||
}
|
||||
|
||||
function delete(bool $softly = true): void
|
||||
|
|
71
Web/Models/Entities/NoSpamLog.php
Normal file
71
Web/Models/Entities/NoSpamLog.php
Normal file
|
@ -0,0 +1,71 @@
|
|||
<?php declare(strict_types=1);
|
||||
namespace openvk\Web\Models\Entities;
|
||||
use openvk\Web\Models\RowModel;
|
||||
use openvk\Web\Util\DateTime;
|
||||
use openvk\Web\Models\Repositories\{Users};
|
||||
use Nette\Database\Table\ActiveRow;
|
||||
|
||||
class NoSpamLog extends RowModel
|
||||
{
|
||||
protected $tableName = "noSpam_templates";
|
||||
|
||||
function getId(): int
|
||||
{
|
||||
return $this->getRecord()->id;
|
||||
}
|
||||
|
||||
function getUser(): ?User
|
||||
{
|
||||
return (new Users)->get($this->getRecord()->user);
|
||||
}
|
||||
|
||||
function getModel(): string
|
||||
{
|
||||
return $this->getRecord()->model;
|
||||
}
|
||||
|
||||
function getRegex(): ?string
|
||||
{
|
||||
return $this->getRecord()->regex;
|
||||
}
|
||||
|
||||
function getRequest(): ?string
|
||||
{
|
||||
return $this->getRecord()->request;
|
||||
}
|
||||
|
||||
function getCount(): int
|
||||
{
|
||||
return $this->getRecord()->count;
|
||||
}
|
||||
|
||||
function getTime(): DateTime
|
||||
{
|
||||
return new DateTime($this->getRecord()->time);
|
||||
}
|
||||
|
||||
function getItems(): ?array
|
||||
{
|
||||
return explode(",", $this->getRecord()->items);
|
||||
}
|
||||
|
||||
function getTypeRaw(): int
|
||||
{
|
||||
return $this->getRecord()->ban_type;
|
||||
}
|
||||
|
||||
function getType(): string
|
||||
{
|
||||
switch ($this->getTypeRaw()) {
|
||||
case 1: return "О";
|
||||
case 2: return "Б";
|
||||
case 3: return "ОБ";
|
||||
default: return (string) $this->getTypeRaw();
|
||||
}
|
||||
}
|
||||
|
||||
function isRollbacked(): bool
|
||||
{
|
||||
return !is_null($this->getRecord()->rollback);
|
||||
}
|
||||
}
|
|
@ -118,4 +118,24 @@ class Note extends Postable
|
|||
{
|
||||
return $this->getRecord()->source;
|
||||
}
|
||||
|
||||
function toVkApiStruct(): object
|
||||
{
|
||||
$res = (object) [];
|
||||
|
||||
$res->type = "note";
|
||||
$res->id = $this->getVirtualId();
|
||||
$res->owner_id = $this->getOwner()->getId();
|
||||
$res->title = $this->getName();
|
||||
$res->text = $this->getText();
|
||||
$res->date = $this->getPublicationTime()->timestamp();
|
||||
$res->comments = $this->getCommentsCount();
|
||||
$res->read_comments = $this->getCommentsCount();
|
||||
$res->view_url = "/note".$this->getOwner()->getId()."_".$this->getId();
|
||||
$res->privacy_view = 1;
|
||||
$res->can_comment = 1;
|
||||
$res->text_wiki = "r";
|
||||
|
||||
return $res;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
<?php declare(strict_types=1);
|
||||
namespace openvk\Web\Models\Entities;
|
||||
use MessagePack\MessagePack;
|
||||
use Nette\Utils\ImageException;
|
||||
use Nette\Utils\UnknownImageFileException;
|
||||
use openvk\Web\Models\Entities\Album;
|
||||
use openvk\Web\Models\Repositories\Albums;
|
||||
use Chandler\Database\DatabaseConnection as DB;
|
||||
|
@ -13,48 +15,62 @@ class Photo extends Media
|
|||
protected $fileExtension = "jpeg";
|
||||
|
||||
const ALLOWED_SIDE_MULTIPLIER = 7;
|
||||
|
||||
private function resizeImage(string $filename, string $outputDir, \SimpleXMLElement $size): array
|
||||
|
||||
/**
|
||||
* @throws \ImagickException
|
||||
* @throws ImageException
|
||||
* @throws UnknownImageFileException
|
||||
*/
|
||||
private function resizeImage(\Imagick $image, string $outputDir, \SimpleXMLElement $size): array
|
||||
{
|
||||
$res = [false];
|
||||
$image = Image::fromFile($filename);
|
||||
$res = [false];
|
||||
$requiresProportion = ((string) $size["requireProp"]) != "none";
|
||||
if($requiresProportion) {
|
||||
$props = explode(":", (string) $size["requireProp"]);
|
||||
$px = (int) $props[0];
|
||||
$py = (int) $props[1];
|
||||
if(($image->getWidth() / $image->getHeight()) > ($px / $py)) {
|
||||
# For some weird reason using resize with EXACT flag causes system to consume an unholy amount of RAM
|
||||
$image->crop(0, 0, "100%", (int) ceil(($px * $image->getWidth()) / $py));
|
||||
if(($image->getImageWidth() / $image->getImageHeight()) > ($px / $py)) {
|
||||
$height = (int) ceil(($px * $image->getImageWidth()) / $py);
|
||||
$image->cropImage($image->getImageWidth(), $height, 0, 0);
|
||||
$res[0] = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if(isset($size["maxSize"])) {
|
||||
$maxSize = (int) $size["maxSize"];
|
||||
$image->resize($maxSize, $maxSize, Image::SHRINK_ONLY | Image::FIT);
|
||||
$sizes = Image::calculateSize($image->getImageWidth(), $image->getImageHeight(), $maxSize, $maxSize, Image::SHRINK_ONLY | Image::FIT);
|
||||
$image->resizeImage($sizes[0], $sizes[1], \Imagick::FILTER_HERMITE, 1);
|
||||
} else if(isset($size["maxResolution"])) {
|
||||
$resolution = explode("x", (string) $size["maxResolution"]);
|
||||
$image->resize((int) $resolution[0], (int) $resolution[1], Image::SHRINK_ONLY | Image::FIT);
|
||||
$sizes = Image::calculateSize(
|
||||
$image->getImageWidth(), $image->getImageHeight(), (int) $resolution[0], (int) $resolution[1], Image::SHRINK_ONLY | Image::FIT
|
||||
);
|
||||
$image->resizeImage($sizes[0], $sizes[1], \Imagick::FILTER_HERMITE, 1);
|
||||
} else {
|
||||
throw new \RuntimeException("Malformed size description: " . (string) $size["id"]);
|
||||
}
|
||||
|
||||
$res[1] = $image->getWidth();
|
||||
$res[2] = $image->getHeight();
|
||||
|
||||
$res[1] = $image->getImageWidth();
|
||||
$res[2] = $image->getImageHeight();
|
||||
if($res[1] <= 300 || $res[2] <= 300)
|
||||
$image->save("$outputDir/" . (string) $size["id"] . ".gif");
|
||||
$image->writeImage("$outputDir/$size[id].gif");
|
||||
else
|
||||
$image->save("$outputDir/" . (string) $size["id"] . ".jpeg");
|
||||
|
||||
imagedestroy($image->getImageResource());
|
||||
$image->writeImage("$outputDir/$size[id].jpeg");
|
||||
|
||||
$res[3] = true;
|
||||
$image->destroy();
|
||||
unset($image);
|
||||
|
||||
|
||||
return $res;
|
||||
}
|
||||
|
||||
private function saveImageResizedCopies(string $filename, string $hash): void
|
||||
private function saveImageResizedCopies(?\Imagick $image, string $filename, string $hash): void
|
||||
{
|
||||
if(!$image) {
|
||||
$image = new \Imagick;
|
||||
$image->readImage($filename);
|
||||
}
|
||||
|
||||
$dir = dirname($this->pathFromHash($hash));
|
||||
$dir = "$dir/$hash" . "_cropped";
|
||||
if(!is_dir($dir)) {
|
||||
|
@ -67,8 +83,13 @@ class Photo extends Media
|
|||
throw new \RuntimeException("Could not load photosizes.xml!");
|
||||
|
||||
$sizesMeta = [];
|
||||
foreach($sizes->Size as $size)
|
||||
$sizesMeta[(string) $size["id"]] = $this->resizeImage($filename, $dir, $size);
|
||||
if(OPENVK_ROOT_CONF["openvk"]["preferences"]["photos"]["photoSaving"] === "quick") {
|
||||
foreach($sizes->Size as $size)
|
||||
$sizesMeta[(string)$size["id"]] = [false, false, false, false];
|
||||
} else {
|
||||
foreach($sizes->Size as $size)
|
||||
$sizesMeta[(string)$size["id"]] = $this->resizeImage(clone $image, $dir, $size);
|
||||
}
|
||||
|
||||
$sizesMeta = MessagePack::pack($sizesMeta);
|
||||
$this->stateChanges("sizes", $sizesMeta);
|
||||
|
@ -76,13 +97,19 @@ class Photo extends Media
|
|||
|
||||
protected function saveFile(string $filename, string $hash): bool
|
||||
{
|
||||
$image = Image::fromFile($filename);
|
||||
if(($image->height >= ($image->width * Photo::ALLOWED_SIDE_MULTIPLIER)) || ($image->width >= ($image->height * Photo::ALLOWED_SIDE_MULTIPLIER)))
|
||||
$image = new \Imagick;
|
||||
$image->readImage($filename);
|
||||
$h = $image->getImageHeight();
|
||||
$w = $image->getImageWidth();
|
||||
if(($h >= ($w * Photo::ALLOWED_SIDE_MULTIPLIER)) || ($w >= ($h * Photo::ALLOWED_SIDE_MULTIPLIER)))
|
||||
throw new ISE("Invalid layout: image is too wide/short");
|
||||
|
||||
$image->resize(8192, 4320, Image::SHRINK_ONLY | Image::FIT);
|
||||
$image->save($this->pathFromHash($hash), 92, Image::JPEG);
|
||||
$this->saveImageResizedCopies($filename, $hash);
|
||||
|
||||
$sizes = Image::calculateSize(
|
||||
$image->getImageWidth(), $image->getImageHeight(), 8192, 4320, Image::SHRINK_ONLY | Image::FIT
|
||||
);
|
||||
$image->resizeImage($sizes[0], $sizes[1], \Imagick::FILTER_HERMITE, 1);
|
||||
$image->writeImage($this->pathFromHash($hash));
|
||||
$this->saveImageResizedCopies($image, $filename, $hash);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -114,8 +141,8 @@ class Photo extends Media
|
|||
$sizes = $this->getRecord()->sizes;
|
||||
if(!$sizes || $forceUpdate) {
|
||||
if($forceUpdate || $upgrade || OPENVK_ROOT_CONF["openvk"]["preferences"]["photos"]["upgradeStructure"]) {
|
||||
$hash = $this->getRecord()->hash;
|
||||
$this->saveImageResizedCopies($this->pathFromHash($hash), $hash);
|
||||
$hash = $this->getRecord()->hash;
|
||||
$this->saveImageResizedCopies(NULL, $this->pathFromHash($hash), $hash);
|
||||
$this->save();
|
||||
|
||||
return $this->getSizes();
|
||||
|
@ -127,6 +154,16 @@ class Photo extends Media
|
|||
$res = [];
|
||||
$sizes = MessagePack::unpack($sizes);
|
||||
foreach($sizes as $id => $meta) {
|
||||
if(isset($meta[3]) && !$meta[3]) {
|
||||
$res[$id] = (object) [
|
||||
"url" => ovk_scheme(true) . $_SERVER["HTTP_HOST"] . "/photos/thumbnails/" . $this->getId() . "_$id.jpeg",
|
||||
"width" => NULL,
|
||||
"height" => NULL,
|
||||
"crop" => NULL
|
||||
];
|
||||
continue;
|
||||
}
|
||||
|
||||
$url = $this->getURL();
|
||||
$url = str_replace(".$this->fileExtension", "_cropped/$id.", $url);
|
||||
$url .= ($meta[1] <= 300 || $meta[2] <= 300) ? "gif" : "jpeg";
|
||||
|
@ -149,6 +186,47 @@ class Photo extends Media
|
|||
|
||||
return $res;
|
||||
}
|
||||
|
||||
function forceSize(string $sizeName): bool
|
||||
{
|
||||
$hash = $this->getRecord()->hash;
|
||||
$sizes = MessagePack::unpack($this->getRecord()->sizes);
|
||||
$size = $sizes[$sizeName] ?? false;
|
||||
if(!$size)
|
||||
return $size;
|
||||
|
||||
if(!isset($size[3]) || $size[3] === true)
|
||||
return true;
|
||||
|
||||
$path = $this->pathFromHash($hash);
|
||||
$dir = dirname($this->pathFromHash($hash));
|
||||
$dir = "$dir/$hash" . "_cropped";
|
||||
if(!is_dir($dir)) {
|
||||
@unlink($dir);
|
||||
mkdir($dir);
|
||||
}
|
||||
|
||||
$sizeMetas = simplexml_load_file(OPENVK_ROOT . "/data/photosizes.xml");
|
||||
if(!$sizeMetas)
|
||||
throw new \RuntimeException("Could not load photosizes.xml!");
|
||||
|
||||
$sizeInfo = NULL;
|
||||
foreach($sizeMetas->Size as $size)
|
||||
if($size["id"] == $sizeName)
|
||||
$sizeInfo = $size;
|
||||
|
||||
if(!$sizeInfo)
|
||||
return false;
|
||||
|
||||
$pic = new \Imagick;
|
||||
$pic->readImage($path);
|
||||
$sizes[$sizeName] = $this->resizeImage($pic, $dir, $sizeInfo);
|
||||
|
||||
$this->stateChanges("sizes", MessagePack::pack($sizes));
|
||||
$this->save();
|
||||
|
||||
return $sizes[$sizeName][3];
|
||||
}
|
||||
|
||||
function getVkApiSizes(): ?array
|
||||
{
|
||||
|
@ -205,30 +283,48 @@ class Photo extends Media
|
|||
return [$x, $y];
|
||||
}
|
||||
|
||||
function getPageURL(): string
|
||||
{
|
||||
if($this->isAnonymous())
|
||||
return "/photos/" . base_convert((string) $this->getId(), 10, 32);
|
||||
|
||||
return "/photo" . $this->getPrettyId();
|
||||
}
|
||||
|
||||
function getAlbum(): ?Album
|
||||
{
|
||||
return (new Albums)->getAlbumByPhotoId($this);
|
||||
}
|
||||
|
||||
function toVkApiStruct(): object
|
||||
function toVkApiStruct(bool $photo_sizes = true, bool $extended = false): object
|
||||
{
|
||||
$res = (object) [];
|
||||
|
||||
$res->id = $res->pid = $this->getId();
|
||||
$res->owner_id = $res->user_id = $this->getOwner()->getId()->getId();
|
||||
$res->id = $res->pid = $this->getVirtualId();
|
||||
$res->owner_id = $res->user_id = $this->getOwner()->getId();
|
||||
$res->aid = $res->album_id = NULL;
|
||||
$res->width = $this->getDimensions()[0];
|
||||
$res->height = $this->getDimensions()[1];
|
||||
$res->date = $res->created = $this->getPublicationTime()->timestamp();
|
||||
|
||||
$res->sizes = $this->getVkApiSizes();
|
||||
$res->src_small = $res->photo_75 = $this->getURLBySizeId("miniscule");
|
||||
$res->src = $res->photo_130 = $this->getURLBySizeId("tiny");
|
||||
$res->src_big = $res->photo_604 = $this->getURLBySizeId("normal");
|
||||
$res->src_xbig = $res->photo_807 = $this->getURLBySizeId("large");
|
||||
$res->src_xxbig = $res->photo_1280 = $this->getURLBySizeId("larger");
|
||||
$res->src_xxxbig = $res->photo_2560 = $this->getURLBySizeId("original");
|
||||
$res->src_original = $res->url = $this->getURLBySizeId("UPLOADED_MAXRES");
|
||||
if($photo_sizes) {
|
||||
$res->sizes = $this->getVkApiSizes();
|
||||
$res->src_small = $res->photo_75 = $this->getURLBySizeId("miniscule");
|
||||
$res->src = $res->photo_130 = $this->getURLBySizeId("tiny");
|
||||
$res->src_big = $res->photo_604 = $this->getURLBySizeId("normal");
|
||||
$res->src_xbig = $res->photo_807 = $this->getURLBySizeId("large");
|
||||
$res->src_xxbig = $res->photo_1280 = $this->getURLBySizeId("larger");
|
||||
$res->src_xxxbig = $res->photo_2560 = $this->getURLBySizeId("original");
|
||||
$res->src_original = $res->url = $this->getURLBySizeId("UPLOADED_MAXRES");
|
||||
}
|
||||
|
||||
if($extended) {
|
||||
$res->likes = $this->getLikesCount(); # их нету но пусть будут
|
||||
$res->comments = $this->getCommentsCount();
|
||||
$res->tags = 0;
|
||||
$res->can_comment = 1;
|
||||
$res->can_repost = 0;
|
||||
}
|
||||
|
||||
return $res;
|
||||
}
|
||||
|
|
|
@ -99,6 +99,11 @@ class Post extends Postable
|
|||
return (($this->getRecord()->flags & 0b00100000) > 0) && ($this->getRecord()->owner > 0);
|
||||
}
|
||||
|
||||
function isUpdateAvatarMessage(): bool
|
||||
{
|
||||
return (($this->getRecord()->flags & 0b00010000) > 0) && ($this->getRecord()->owner > 0);
|
||||
}
|
||||
|
||||
function isExplicit(): bool
|
||||
{
|
||||
return (bool) $this->getRecord()->nsfw;
|
||||
|
@ -113,6 +118,63 @@ class Post extends Postable
|
|||
{
|
||||
return $this->getOwner(false)->getId();
|
||||
}
|
||||
|
||||
function getPlatform(bool $forAPI = false): ?string
|
||||
{
|
||||
$platform = $this->getRecord()->api_source_name;
|
||||
if($forAPI) {
|
||||
switch ($platform) {
|
||||
case 'openvk_refresh_android':
|
||||
case 'openvk_legacy_android':
|
||||
return 'android';
|
||||
break;
|
||||
|
||||
case 'openvk_ios':
|
||||
case 'openvk_legacy_ios':
|
||||
return 'iphone';
|
||||
break;
|
||||
|
||||
case 'vika_touch': // кика хохотач ахахахаххахахахахах
|
||||
case 'vk4me':
|
||||
return 'mobile';
|
||||
break;
|
||||
|
||||
case NULL:
|
||||
return NULL;
|
||||
break;
|
||||
|
||||
default:
|
||||
return 'api';
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
return $platform;
|
||||
}
|
||||
}
|
||||
|
||||
function getPlatformDetails(): array
|
||||
{
|
||||
$clients = simplexml_load_file(OPENVK_ROOT . "/data/clients.xml");
|
||||
|
||||
foreach($clients as $client) {
|
||||
if($client['tag'] == $this->getPlatform()) {
|
||||
return [
|
||||
"tag" => $client['tag'],
|
||||
"name" => $client['name'],
|
||||
"url" => $client['url'],
|
||||
"img" => $client['img']
|
||||
];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
"tag" => $this->getPlatform(),
|
||||
"name" => NULL,
|
||||
"url" => NULL,
|
||||
"img" => NULL
|
||||
];
|
||||
}
|
||||
|
||||
function pin(): void
|
||||
{
|
||||
|
@ -183,6 +245,20 @@ class Post extends Postable
|
|||
$this->unwire();
|
||||
$this->save();
|
||||
}
|
||||
|
||||
function canBeEditedBy(?User $user = NULL): bool
|
||||
{
|
||||
if(!$user)
|
||||
return false;
|
||||
|
||||
if($this->isDeactivationMessage() || $this->isUpdateAvatarMessage())
|
||||
return false;
|
||||
|
||||
if($this->getTargetWall() > 0)
|
||||
return $this->getPublicationTime()->timestamp() + WEEK > time() && $user->getId() == $this->getOwner(false)->getId();
|
||||
|
||||
return $user->getId() == $this->getOwner(false)->getId();
|
||||
}
|
||||
|
||||
use Traits\TRichText;
|
||||
}
|
||||
|
|
|
@ -34,7 +34,8 @@ abstract class Postable extends Attachable
|
|||
$oid = (int) $this->getRecord()->owner;
|
||||
if(!$real && $this->isAnonymous())
|
||||
$oid = OPENVK_ROOT_CONF["openvk"]["preferences"]["wall"]["anonymousPosting"]["account"];
|
||||
|
||||
|
||||
$oid = abs($oid);
|
||||
if($oid > 0)
|
||||
return (new Users)->get($oid);
|
||||
else
|
||||
|
@ -84,7 +85,7 @@ abstract class Postable extends Attachable
|
|||
return sizeof(DB::i()->getContext()->table("likes")->where([
|
||||
"model" => static::class,
|
||||
"target" => $this->getRecord()->id,
|
||||
]));
|
||||
])->group("origin"));
|
||||
}
|
||||
|
||||
# TODO add pagination
|
||||
|
@ -151,7 +152,7 @@ abstract class Postable extends Attachable
|
|||
throw new ISE("Setting virtual id manually is forbidden");
|
||||
}
|
||||
|
||||
function save(): void
|
||||
function save(?bool $log = false): void
|
||||
{
|
||||
$vref = $this->upperNodeReferenceColumnName;
|
||||
|
||||
|
@ -166,11 +167,11 @@ abstract class Postable extends Attachable
|
|||
$this->stateChanges("created", time());
|
||||
|
||||
$this->stateChanges("virtual_id", $pCount + 1);
|
||||
} else {
|
||||
} /*else {
|
||||
$this->stateChanges("edited", time());
|
||||
}
|
||||
}*/
|
||||
|
||||
parent::save();
|
||||
parent::save($log);
|
||||
}
|
||||
|
||||
use Traits\TAttachmentHost;
|
||||
|
|
154
Web/Models/Entities/Report.php
Normal file
154
Web/Models/Entities/Report.php
Normal file
|
@ -0,0 +1,154 @@
|
|||
<?php declare(strict_types=1);
|
||||
namespace openvk\Web\Models\Entities;
|
||||
use openvk\Web\Util\DateTime;
|
||||
use Nette\Database\Table\ActiveRow;
|
||||
use openvk\Web\Models\RowModel;
|
||||
use openvk\Web\Models\Entities\Club;
|
||||
use Chandler\Database\DatabaseConnection;
|
||||
use openvk\Web\Models\Repositories\{Applications, Comments, Notes, Reports, Users, Posts, Photos, Videos, Clubs};
|
||||
use Chandler\Database\DatabaseConnection as DB;
|
||||
use Nette\InvalidStateException as ISE;
|
||||
use Nette\Database\Table\Selection;
|
||||
|
||||
class Report extends RowModel
|
||||
{
|
||||
protected $tableName = "reports";
|
||||
|
||||
function getId(): int
|
||||
{
|
||||
return $this->getRecord()->id;
|
||||
}
|
||||
|
||||
function getStatus(): int
|
||||
{
|
||||
return $this->getRecord()->status;
|
||||
}
|
||||
|
||||
function getContentType(): string
|
||||
{
|
||||
return $this->getRecord()->type;
|
||||
}
|
||||
|
||||
function getReason(): string
|
||||
{
|
||||
return $this->getRecord()->reason;
|
||||
}
|
||||
|
||||
function getTime(): DateTime
|
||||
{
|
||||
return new DateTime($this->getRecord()->date);
|
||||
}
|
||||
|
||||
function isDeleted(): bool
|
||||
{
|
||||
if ($this->getRecord()->deleted === 0)
|
||||
{
|
||||
return false;
|
||||
} elseif ($this->getRecord()->deleted === 1) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
function authorId(): int
|
||||
{
|
||||
return $this->getRecord()->user_id;
|
||||
}
|
||||
|
||||
function getUser(): User
|
||||
{
|
||||
return (new Users)->get((int) $this->getRecord()->user_id);
|
||||
}
|
||||
|
||||
function getContentId(): int
|
||||
{
|
||||
return (int) $this->getRecord()->target_id;
|
||||
}
|
||||
|
||||
function getContentObject()
|
||||
{
|
||||
if ($this->getContentType() == "post") return (new Posts)->get($this->getContentId());
|
||||
else if ($this->getContentType() == "photo") return (new Photos)->get($this->getContentId());
|
||||
else if ($this->getContentType() == "video") return (new Videos)->get($this->getContentId());
|
||||
else if ($this->getContentType() == "group") return (new Clubs)->get($this->getContentId());
|
||||
else if ($this->getContentType() == "comment") return (new Comments)->get($this->getContentId());
|
||||
else if ($this->getContentType() == "note") return (new Notes)->get($this->getContentId());
|
||||
else if ($this->getContentType() == "app") return (new Applications)->get($this->getContentId());
|
||||
else if ($this->getContentType() == "user") return (new Users)->get($this->getContentId());
|
||||
else return null;
|
||||
}
|
||||
|
||||
function getAuthor(): RowModel
|
||||
{
|
||||
return (new Posts)->get($this->getContentId())->getOwner();
|
||||
}
|
||||
|
||||
function getReportAuthor(): User
|
||||
{
|
||||
return (new Users)->get($this->getRecord()->user_id);
|
||||
}
|
||||
|
||||
function banUser($initiator)
|
||||
{
|
||||
$reason = $this->getContentType() !== "user" ? ("**content-" . $this->getContentType() . "-" . $this->getContentId() . "**") : ("Подозрительная активность");
|
||||
$this->getAuthor()->ban($reason, false, time() + $this->getAuthor()->getNewBanTime(), $initiator);
|
||||
}
|
||||
|
||||
function deleteContent()
|
||||
{
|
||||
if ($this->getContentType() !== "user") {
|
||||
$pubTime = $this->getContentObject()->getPublicationTime();
|
||||
if (method_exists($this->getContentObject(), "getName")) {
|
||||
$name = $this->getContentObject()->getName();
|
||||
$placeholder = "$pubTime ($name)";
|
||||
} else {
|
||||
$placeholder = "$pubTime";
|
||||
}
|
||||
|
||||
if ($this->getAuthor() instanceof Club) {
|
||||
$name = $this->getAuthor()->getName();
|
||||
$this->getAuthor()->getOwner()->adminNotify("Ваш контент, который опубликовали $placeholder в созданной вами группе \"$name\" был удалён модераторами инстанса. За повторные или серьёзные нарушения группу могут заблокировать.");
|
||||
} else {
|
||||
$this->getAuthor()->adminNotify("Ваш контент, который вы опубликовали $placeholder был удалён модераторами инстанса. За повторные или серьёзные нарушения вас могут заблокировать.");
|
||||
}
|
||||
$this->getContentObject()->delete($this->getContentType() !== "app");
|
||||
}
|
||||
|
||||
$this->delete();
|
||||
}
|
||||
|
||||
function getDuplicates(): \Traversable
|
||||
{
|
||||
return (new Reports)->getDuplicates($this->getContentType(), $this->getContentId(), $this->getId());
|
||||
}
|
||||
|
||||
function getDuplicatesCount(): int
|
||||
{
|
||||
return count(iterator_to_array($this->getDuplicates()));
|
||||
}
|
||||
|
||||
function hasDuplicates(): bool
|
||||
{
|
||||
return $this->getDuplicatesCount() > 0;
|
||||
}
|
||||
|
||||
function getContentName(): string
|
||||
{
|
||||
if (method_exists($this->getContentObject(), "getCanonicalName"))
|
||||
return $this->getContentObject()->getCanonicalName();
|
||||
|
||||
return $this->getContentType() . " #" . $this->getContentId();
|
||||
}
|
||||
|
||||
public function delete(bool $softly = true): void
|
||||
{
|
||||
if ($this->hasDuplicates()) {
|
||||
foreach ($this->getDuplicates() as $duplicate) {
|
||||
$duplicate->setDeleted(1);
|
||||
$duplicate->save();
|
||||
}
|
||||
}
|
||||
|
||||
$this->setDeleted(1);
|
||||
$this->save();
|
||||
}
|
||||
}
|
|
@ -68,6 +68,12 @@ class Topic extends Postable
|
|||
return isset($array[0]) ? $array[0] : NULL;
|
||||
}
|
||||
|
||||
function getFirstComment(): ?Comment
|
||||
{
|
||||
$array = iterator_to_array($this->getComments(1));
|
||||
return $array[0] ?? NULL;
|
||||
}
|
||||
|
||||
function getUpdateTime(): DateTime
|
||||
{
|
||||
$lastComment = $this->getLastComment();
|
||||
|
@ -83,4 +89,40 @@ class Topic extends Postable
|
|||
$this->unwire();
|
||||
$this->save();
|
||||
}
|
||||
|
||||
function toVkApiStruct(int $preview = 0, int $preview_length = 90): object
|
||||
{
|
||||
$res = (object)[];
|
||||
|
||||
$res->id = $this->getId();
|
||||
$res->title = $this->getTitle();
|
||||
$res->created = $this->getPublicationTime()->timestamp();
|
||||
|
||||
if($this->getOwner() instanceof User) {
|
||||
$res->created_by = $this->getOwner()->getId();
|
||||
} else {
|
||||
$res->created_by = $this->getOwner()->getId() * -1;
|
||||
}
|
||||
|
||||
$res->updated = $this->getUpdateTime()->timestamp();
|
||||
|
||||
if($this->getLastComment()) {
|
||||
if($this->getLastComment()->getOwner() instanceof User) {
|
||||
$res->updated_by = $this->getLastComment()->getOwner()->getId();
|
||||
} else {
|
||||
$res->updated_by = $this->getLastComment()->getOwner()->getId() * -1;
|
||||
}
|
||||
}
|
||||
|
||||
$res->is_closed = (int)$this->isClosed();
|
||||
$res->is_fixed = (int)$this->isPinned();
|
||||
$res->comments = $this->getCommentsCount();
|
||||
|
||||
if($preview == 1) {
|
||||
$res->first_comment = $this->getFirstComment() ? ovk_proc_strtr($this->getFirstComment()->getText(false), $preview_length) : NULL;
|
||||
$res->last_comment = $this->getLastComment() ? ovk_proc_strtr($this->getLastComment()->getText(false), $preview_length) : NULL;
|
||||
}
|
||||
|
||||
return $res;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
<?php declare(strict_types=1);
|
||||
namespace openvk\Web\Models\Entities\Traits;
|
||||
use openvk\Web\Models\Entities\Attachable;
|
||||
use openvk\Web\Models\Entities\{Attachable, Photo};
|
||||
use openvk\Web\Util\Makima\Makima;
|
||||
use Chandler\Database\DatabaseConnection;
|
||||
|
||||
trait TAttachmentHost
|
||||
|
@ -29,6 +30,46 @@ trait TAttachmentHost
|
|||
yield $repo->get($rel->attachable_id);
|
||||
}
|
||||
}
|
||||
|
||||
function getChildrenWithLayout(int $w, int $h = -1): object
|
||||
{
|
||||
if($h < 0)
|
||||
$h = $w;
|
||||
|
||||
$children = $this->getChildren();
|
||||
$skipped = $photos = $result = [];
|
||||
foreach($children as $child) {
|
||||
if($child instanceof Photo) {
|
||||
$photos[] = $child;
|
||||
continue;
|
||||
}
|
||||
|
||||
$skipped[] = $child;
|
||||
}
|
||||
|
||||
$height = "unset";
|
||||
$width = $w;
|
||||
if(sizeof($photos) < 2) {
|
||||
if(isset($photos[0]))
|
||||
$result[] = ["100%", "unset", $photos[0], "unset"];
|
||||
} else {
|
||||
$mak = new Makima($photos);
|
||||
$layout = $mak->computeMasonryLayout($w, $h);
|
||||
$height = $layout->height;
|
||||
$width = $layout->width;
|
||||
for($i = 0; $i < sizeof($photos); $i++) {
|
||||
$tile = $layout->tiles[$i];
|
||||
$result[] = [$tile->width . "px", $tile->height . "px", $photos[$i], "left"];
|
||||
}
|
||||
}
|
||||
|
||||
return (object) [
|
||||
"width" => $width . "px",
|
||||
"height" => $height . "px",
|
||||
"tiles" => $result,
|
||||
"extras" => $skipped,
|
||||
];
|
||||
}
|
||||
|
||||
function attach(Attachable $attachment): void
|
||||
{
|
||||
|
|
|
@ -36,9 +36,9 @@ trait TRichText
|
|||
"%(([A-z]++):\/\/(\S*?\.\S*?))([\s)\[\]{},\"\'<]|\.\s|$)%",
|
||||
(function (array $matches): string {
|
||||
$href = str_replace("#", "#", $matches[1]);
|
||||
$href = rawurlencode(str_replace(";", ";", $matches[1]));
|
||||
$href = rawurlencode(str_replace(";", ";", $href));
|
||||
$link = str_replace("#", "#", $matches[3]);
|
||||
$link = str_replace(";", ";", $matches[3]);
|
||||
$link = str_replace(";", ";", $link);
|
||||
$rel = $this->isAd() ? "sponsored" : "ugc";
|
||||
|
||||
return "<a href='/away.php?to=$href' rel='$rel' target='_blank'>$link</a>" . htmlentities($matches[4]);
|
||||
|
@ -49,7 +49,7 @@ trait TRichText
|
|||
|
||||
private function removeZalgo(string $text): string
|
||||
{
|
||||
return preg_replace("%[\x{0300}-\x{036F}]{3,}%Xu", "<EFBFBD>", $text);
|
||||
return preg_replace("%\p{M}{3,}%Xu", "", $text);
|
||||
}
|
||||
|
||||
function resolveMentions(array $skipUsers = []): \Traversable
|
||||
|
|
|
@ -5,7 +5,7 @@ use openvk\Web\Themes\{Themepack, Themepacks};
|
|||
use openvk\Web\Util\DateTime;
|
||||
use openvk\Web\Models\RowModel;
|
||||
use openvk\Web\Models\Entities\{Photo, Message, Correspondence, Gift};
|
||||
use openvk\Web\Models\Repositories\{Photos, Users, Clubs, Albums, Gifts, Notifications};
|
||||
use openvk\Web\Models\Repositories\{Applications, Bans, Comments, Notes, Posts, Users, Clubs, Albums, Gifts, Notifications, Videos, Photos};
|
||||
use openvk\Web\Models\Exceptions\InvalidUserNameException;
|
||||
use Nette\Database\Table\ActiveRow;
|
||||
use Chandler\Database\DatabaseConnection;
|
||||
|
@ -39,11 +39,14 @@ class User extends RowModel
|
|||
$query = "SELECT id FROM\n" . file_get_contents(__DIR__ . "/../sql/$filename.tsql");
|
||||
$query .= "\n LIMIT " . $limit . " OFFSET " . ( ($page - 1) * $limit );
|
||||
|
||||
$ids = [];
|
||||
$rels = DatabaseConnection::i()->getConnection()->query($query, $id, $id);
|
||||
foreach($rels as $rel) {
|
||||
$rel = (new Users)->get($rel->id);
|
||||
if(!$rel) continue;
|
||||
|
||||
if(in_array($rel->getId(), $ids)) continue;
|
||||
$ids[] = $rel->getId();
|
||||
|
||||
yield $rel;
|
||||
}
|
||||
}
|
||||
|
@ -238,11 +241,60 @@ class User extends RowModel
|
|||
return $this->getRecord()->alert;
|
||||
}
|
||||
|
||||
function getBanReason(): ?string
|
||||
function getTextForContentBan(string $type): string
|
||||
{
|
||||
switch ($type) {
|
||||
case "post": return "за размещение от Вашего лица таких <b>записей</b>:";
|
||||
case "photo": return "за размещение от Вашего лица таких <b>фотографий</b>:";
|
||||
case "video": return "за размещение от Вашего лица таких <b>видеозаписей</b>:";
|
||||
case "group": return "за подозрительное вступление от Вашего лица <b>в группу:</b>";
|
||||
case "comment": return "за размещение от Вашего лица таких <b>комментариев</b>:";
|
||||
case "note": return "за размещение от Вашего лица таких <b>заметок</b>:";
|
||||
case "app": return "за создание от Вашего имени <b>подозрительных приложений</b>.";
|
||||
default: return "за размещение от Вашего лица такого <b>контента</b>:";
|
||||
}
|
||||
}
|
||||
|
||||
function getRawBanReason(): ?string
|
||||
{
|
||||
return $this->getRecord()->block_reason;
|
||||
}
|
||||
|
||||
function getBanReason(?string $for = null)
|
||||
{
|
||||
$ban = (new Bans)->get((int) $this->getRecord()->block_reason);
|
||||
if (!$ban || $ban->isOver()) return null;
|
||||
|
||||
$reason = $ban->getReason();
|
||||
|
||||
preg_match('/\*\*content-(post|photo|video|group|comment|note|app|noSpamTemplate|user)-(\d+)\*\*$/', $reason, $matches);
|
||||
if (sizeof($matches) === 3) {
|
||||
$content_type = $matches[1]; $content_id = (int) $matches[2];
|
||||
if (in_array($content_type, ["noSpamTemplate", "user"])) {
|
||||
$reason = "Подозрительная активность";
|
||||
} else {
|
||||
if ($for !== "banned") {
|
||||
$reason = "Подозрительная активность";
|
||||
} else {
|
||||
$reason = [$this->getTextForContentBan($content_type), $content_type];
|
||||
switch ($content_type) {
|
||||
case "post": $reason[] = (new Posts)->get($content_id); break;
|
||||
case "photo": $reason[] = (new Photos)->get($content_id); break;
|
||||
case "video": $reason[] = (new Videos)->get($content_id); break;
|
||||
case "group": $reason[] = (new Clubs)->get($content_id); break;
|
||||
case "comment": $reason[] = (new Comments)->get($content_id); break;
|
||||
case "note": $reason[] = (new Notes)->get($content_id); break;
|
||||
case "app": $reason[] = (new Applications)->get($content_id); break;
|
||||
case "user": $reason[] = (new Users)->get($content_id); break;
|
||||
default: $reason[] = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $reason;
|
||||
}
|
||||
|
||||
function getBanInSupportReason(): ?string
|
||||
{
|
||||
return $this->getRecord()->block_in_support_reason;
|
||||
|
@ -411,6 +463,7 @@ class User extends RowModel
|
|||
"news",
|
||||
"links",
|
||||
"poster",
|
||||
"apps"
|
||||
],
|
||||
])->get($id);
|
||||
}
|
||||
|
@ -752,6 +805,63 @@ class User extends RowModel
|
|||
return time() - $this->getRecord()->online <= 300;
|
||||
}
|
||||
|
||||
function getOnlinePlatform(bool $forAPI = false): ?string
|
||||
{
|
||||
$platform = $this->getRecord()->client_name;
|
||||
if($forAPI) {
|
||||
switch ($platform) {
|
||||
case 'openvk_refresh_android':
|
||||
case 'openvk_legacy_android':
|
||||
return 'android';
|
||||
break;
|
||||
|
||||
case 'openvk_ios':
|
||||
case 'openvk_legacy_ios':
|
||||
return 'iphone';
|
||||
break;
|
||||
|
||||
case 'vika_touch': // кика хохотач ахахахаххахахахахах
|
||||
case 'vk4me':
|
||||
return 'mobile';
|
||||
break;
|
||||
|
||||
case NULL:
|
||||
return NULL;
|
||||
break;
|
||||
|
||||
default:
|
||||
return 'api';
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
return $platform;
|
||||
}
|
||||
}
|
||||
|
||||
function getOnlinePlatformDetails(): array
|
||||
{
|
||||
$clients = simplexml_load_file(OPENVK_ROOT . "/data/clients.xml");
|
||||
|
||||
foreach($clients as $client) {
|
||||
if($client['tag'] == $this->getOnlinePlatform()) {
|
||||
return [
|
||||
"tag" => $client['tag'],
|
||||
"name" => $client['name'],
|
||||
"url" => $client['url'],
|
||||
"img" => $client['img']
|
||||
];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
"tag" => $this->getOnlinePlatform(),
|
||||
"name" => NULL,
|
||||
"url" => NULL,
|
||||
"img" => NULL
|
||||
];
|
||||
}
|
||||
|
||||
function prefersNotToSeeRating(): bool
|
||||
{
|
||||
return !((bool) $this->getRecord()->show_rating);
|
||||
|
@ -774,7 +884,7 @@ class User extends RowModel
|
|||
]);
|
||||
}
|
||||
|
||||
function ban(string $reason, bool $deleteSubscriptions = true, ?int $unban_time = NULL): void
|
||||
function ban(string $reason, bool $deleteSubscriptions = true, $unban_time = NULL, ?int $initiator = NULL): void
|
||||
{
|
||||
if($deleteSubscriptions) {
|
||||
$subs = DatabaseConnection::i()->getContext()->table("subscriptions");
|
||||
|
@ -787,8 +897,33 @@ class User extends RowModel
|
|||
$subs->delete();
|
||||
}
|
||||
|
||||
$this->setBlock_Reason($reason);
|
||||
$this->setUnblock_time($unban_time);
|
||||
$iat = time();
|
||||
$ban = new Ban;
|
||||
$ban->setUser($this->getId());
|
||||
$ban->setReason($reason);
|
||||
$ban->setInitiator($initiator);
|
||||
$ban->setIat($iat);
|
||||
$ban->setExp($unban_time !== "permanent" ? $unban_time : 0);
|
||||
$ban->setTime($unban_time === "permanent" ? 0 : ($unban_time ? ($unban_time - $iat) : 0));
|
||||
$ban->save();
|
||||
|
||||
$this->setBlock_Reason($ban->getId());
|
||||
// $this->setUnblock_time($unban_time);
|
||||
$this->save();
|
||||
}
|
||||
|
||||
function unban(int $removed_by): void
|
||||
{
|
||||
$ban = (new Bans)->get((int) $this->getRawBanReason());
|
||||
if (!$ban || $ban->isOver())
|
||||
return;
|
||||
|
||||
$ban->setRemoved_Manually(true);
|
||||
$ban->setRemoved_By($removed_by);
|
||||
$ban->save();
|
||||
|
||||
$this->setBlock_Reason(NULL);
|
||||
// $user->setUnblock_time(NULL);
|
||||
$this->save();
|
||||
}
|
||||
|
||||
|
@ -893,6 +1028,7 @@ class User extends RowModel
|
|||
"news",
|
||||
"links",
|
||||
"poster",
|
||||
"apps"
|
||||
],
|
||||
])->set($id, (int) $status)->toInteger();
|
||||
|
||||
|
@ -953,6 +1089,15 @@ class User extends RowModel
|
|||
return true;
|
||||
}
|
||||
|
||||
function updOnline(string $platform): bool
|
||||
{
|
||||
$this->setOnline(time());
|
||||
$this->setClient_name($platform);
|
||||
$this->save(false);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function changeEmail(string $email): void
|
||||
{
|
||||
DatabaseConnection::i()->getContext()->table("ChandlerUsers")
|
||||
|
@ -966,7 +1111,7 @@ class User extends RowModel
|
|||
|
||||
function adminNotify(string $message): bool
|
||||
{
|
||||
$admId = OPENVK_ROOT_CONF["openvk"]["preferences"]["support"]["adminAccount"];
|
||||
$admId = (int) OPENVK_ROOT_CONF["openvk"]["preferences"]["support"]["adminAccount"];
|
||||
if(!$admId)
|
||||
return false;
|
||||
else if(is_null($admin = (new Users)->get($admId)))
|
||||
|
@ -1031,7 +1176,11 @@ class User extends RowModel
|
|||
|
||||
function getUnbanTime(): ?string
|
||||
{
|
||||
return !is_null($this->getRecord()->unblock_time) ? date('d.m.Y', $this->getRecord()->unblock_time) : NULL;
|
||||
$ban = (new Bans)->get((int) $this->getRecord()->block_reason);
|
||||
if (!$ban || $ban->isOver() || $ban->isPermanent()) return null;
|
||||
if ($this->canUnbanThemself()) return tr("today");
|
||||
|
||||
return date('d.m.Y', $ban->getEndTime());
|
||||
}
|
||||
|
||||
function canUnbanThemself(): bool
|
||||
|
@ -1039,10 +1188,57 @@ class User extends RowModel
|
|||
if (!$this->isBanned())
|
||||
return false;
|
||||
|
||||
if ($this->getRecord()->unblock_time > time() || $this->getRecord()->unblock_time == 0)
|
||||
return false;
|
||||
$ban = (new Bans)->get((int) $this->getRecord()->block_reason);
|
||||
if (!$ban || $ban->isOver() || $ban->isPermanent()) return false;
|
||||
|
||||
return true;
|
||||
return $ban->getEndTime() <= time() && !$ban->isPermanent();
|
||||
}
|
||||
|
||||
function getNewBanTime()
|
||||
{
|
||||
$bans = iterator_to_array((new Bans)->getByUser($this->getid()));
|
||||
if (!$bans || count($bans) === 0)
|
||||
return 0;
|
||||
|
||||
$last_ban = end($bans);
|
||||
if (!$last_ban) return 0;
|
||||
|
||||
if ($last_ban->isPermanent()) return "permanent";
|
||||
|
||||
$values = [0, 3600, 7200, 86400, 172800, 604800, 1209600, 3024000, 9072000];
|
||||
$response = 0;
|
||||
$i = 0;
|
||||
|
||||
foreach ($values as $value) {
|
||||
$i++;
|
||||
if ($last_ban->getTime() === 0 && $value === 0) continue;
|
||||
if ($last_ban->getTime() < $value) {
|
||||
$response = $value;
|
||||
break;
|
||||
} else if ($last_ban->getTime() >= $value) {
|
||||
if ($i < count($values)) continue;
|
||||
$response = "permanent";
|
||||
break;
|
||||
}
|
||||
}
|
||||
return $response;
|
||||
}
|
||||
|
||||
function toVkApiStruct(): object
|
||||
{
|
||||
$res = (object) [];
|
||||
|
||||
$res->id = $this->getId();
|
||||
$res->first_name = $this->getFirstName();
|
||||
$res->last_name = $this->getLastName();
|
||||
$res->deactivated = $this->isDeactivated();
|
||||
$res->photo_50 = $this->getAvatarURL();
|
||||
$res->photo_100 = $this->getAvatarURL("tiny");
|
||||
$res->photo_200 = $this->getAvatarURL("normal");
|
||||
$res->photo_id = !is_null($this->getAvatarPhoto()) ? $this->getAvatarPhoto()->getPrettyId() : NULL;
|
||||
# TODO: Perenesti syuda vsyo ostalnoyie
|
||||
|
||||
return $res;
|
||||
}
|
||||
|
||||
use Traits\TBackDrops;
|
||||
|
|
|
@ -13,7 +13,7 @@ class Video extends Media
|
|||
const TYPE_EMBED = 1;
|
||||
|
||||
protected $tableName = "videos";
|
||||
protected $fileExtension = "ogv";
|
||||
protected $fileExtension = "mp4";
|
||||
|
||||
protected $processingPlaceholder = "video/rendering";
|
||||
|
||||
|
@ -30,7 +30,7 @@ class Video extends Media
|
|||
throw new \DomainException("$filename does not contain any video streams");
|
||||
|
||||
$durations = [];
|
||||
preg_match('%duration=([0-9\.]++)%', $streams, $durations);
|
||||
preg_match_all('%duration=([0-9\.]++)%', $streams, $durations);
|
||||
if(sizeof($durations[1]) === 0)
|
||||
throw new \DomainException("$filename does not contain any meaningful video streams");
|
||||
|
||||
|
@ -104,7 +104,7 @@ class Video extends Media
|
|||
if(!$this->isProcessed())
|
||||
return "/assets/packages/static/openvk/video/rendering.apng";
|
||||
|
||||
return preg_replace("%\.[A-z]++$%", ".gif", $this->getURL());
|
||||
return preg_replace("%\.[A-z0-9]++$%", ".gif", $this->getURL());
|
||||
} else {
|
||||
return $this->getVideoDriver()->getThumbnailURL();
|
||||
}
|
||||
|
@ -114,7 +114,64 @@ class Video extends Media
|
|||
{
|
||||
return $this->getRecord()->owner;
|
||||
}
|
||||
|
||||
function getApiStructure(): object
|
||||
{
|
||||
$fromYoutube = $this->getType() == Video::TYPE_EMBED;
|
||||
return (object)[
|
||||
"type" => "video",
|
||||
"video" => [
|
||||
"can_comment" => 1,
|
||||
"can_like" => 0, // we don't h-have wikes in videos
|
||||
"can_repost" => 0,
|
||||
"can_subscribe" => 1,
|
||||
"can_add_to_faves" => 0,
|
||||
"can_add" => 0,
|
||||
"comments" => $this->getCommentsCount(),
|
||||
"date" => $this->getPublicationTime()->timestamp(),
|
||||
"description" => $this->getDescription(),
|
||||
"duration" => 0, // я хуй знает как получить длину видео
|
||||
"image" => [
|
||||
[
|
||||
"url" => $this->getThumbnailURL(),
|
||||
"width" => 320,
|
||||
"height" => 240,
|
||||
"with_padding" => 1
|
||||
]
|
||||
],
|
||||
"width" => 640,
|
||||
"height" => 480,
|
||||
"id" => $this->getVirtualId(),
|
||||
"owner_id" => $this->getOwner()->getId(),
|
||||
"user_id" => $this->getOwner()->getId(),
|
||||
"title" => $this->getName(),
|
||||
"is_favorite" => false,
|
||||
"player" => !$fromYoutube ? $this->getURL() : $this->getVideoDriver()->getURL(),
|
||||
"files" => !$fromYoutube ? [
|
||||
"mp4_480" => $this->getURL()
|
||||
] : NULL,
|
||||
"platform" => $fromYoutube ? "youtube" : NULL,
|
||||
"added" => 0,
|
||||
"repeat" => 0,
|
||||
"type" => "video",
|
||||
"views" => 0,
|
||||
"likes" => [
|
||||
"count" => 0,
|
||||
"user_likes" => 0
|
||||
],
|
||||
"reposts" => [
|
||||
"count" => 0,
|
||||
"user_reposted" => 0
|
||||
]
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
function toVkApiStruct(): object
|
||||
{
|
||||
return $this->getApiStructure();
|
||||
}
|
||||
|
||||
function setLink(string $link): string
|
||||
{
|
||||
if(preg_match(file_get_contents(__DIR__ . "/../VideoDrivers/regex/youtube.txt"), $link, $matches)) {
|
||||
|
@ -145,11 +202,14 @@ class Video extends Media
|
|||
$this->save();
|
||||
}
|
||||
|
||||
static function fastMake(int $owner, string $description = "", array $file, bool $unlisted = true, bool $anon = false): Video
|
||||
static function fastMake(int $owner, string $name = "Unnamed Video.ogv", string $description = "", array $file, bool $unlisted = true, bool $anon = false): Video
|
||||
{
|
||||
if(OPENVK_ROOT_CONF['openvk']['preferences']['videos']['disableUploading'])
|
||||
exit(VIDEOS_FRIENDLY_ERROR);
|
||||
|
||||
$video = new Video;
|
||||
$video->setOwner($owner);
|
||||
$video->setName("Unnamed Video.ogv");
|
||||
$video->setName(ovk_proc_strtr($name, 61));
|
||||
$video->setDescription(ovk_proc_strtr($description, 300));
|
||||
$video->setAnonymous($anon);
|
||||
$video->setCreated(time());
|
||||
|
|
|
@ -123,4 +123,14 @@ class Albums
|
|||
|
||||
return $dbalbum->collection ? $this->get($dbalbum->collection) : null;
|
||||
}
|
||||
|
||||
function getAlbumByOwnerAndId(int $owner, int $id)
|
||||
{
|
||||
$album = $this->albums->where([
|
||||
"owner" => $owner,
|
||||
"id" => $id
|
||||
])->fetch();
|
||||
|
||||
return new Album($album);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -66,4 +66,12 @@ class Applications
|
|||
{
|
||||
return sizeof($this->appRels->where("user", $user->getId()));
|
||||
}
|
||||
|
||||
function find(string $query, array $pars = [], string $sort = "id"): Util\EntityStream
|
||||
{
|
||||
$query = "%$query%";
|
||||
$result = $this->apps->where("CONCAT_WS(' ', name, description) LIKE ?", $query)->where("enabled", 1);
|
||||
|
||||
return new Util\EntityStream("Application", $result->order("$sort"));
|
||||
}
|
||||
}
|
33
Web/Models/Repositories/Bans.php
Normal file
33
Web/Models/Repositories/Bans.php
Normal file
|
@ -0,0 +1,33 @@
|
|||
<?php declare(strict_types=1);
|
||||
namespace openvk\Web\Models\Repositories;
|
||||
use Chandler\Database\DatabaseConnection as DB;
|
||||
use Nette\Database\Table\{ActiveRow, Selection};
|
||||
use openvk\Web\Models\Entities\Ban;
|
||||
|
||||
class Bans
|
||||
{
|
||||
private $context;
|
||||
private $bans;
|
||||
|
||||
function __construct()
|
||||
{
|
||||
$this->context = DB::i()->getContext();
|
||||
$this->bans = $this->context->table("bans");
|
||||
}
|
||||
|
||||
function toBan(?ActiveRow $ar): ?Ban
|
||||
{
|
||||
return is_null($ar) ? NULL : new Ban($ar);
|
||||
}
|
||||
|
||||
function get(int $id): ?Ban
|
||||
{
|
||||
return $this->toBan($this->bans->get($id));
|
||||
}
|
||||
|
||||
function getByUser(int $user_id): \Traversable
|
||||
{
|
||||
foreach ($this->bans->where("user", $user_id) as $ban)
|
||||
yield new Ban($ban);
|
||||
}
|
||||
}
|
|
@ -28,7 +28,8 @@ class ChandlerUsers
|
|||
|
||||
function getById(string $UUID): ?ChandlerUser
|
||||
{
|
||||
return new ChandlerUser($this->users->where("id", $UUID)->fetch());
|
||||
$user = $this->users->where("id", $UUID)->fetch();
|
||||
return $user ? new ChandlerUser($user) : NULL;
|
||||
}
|
||||
|
||||
function getList(int $page = 1): \Traversable
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<?php declare(strict_types=1);
|
||||
namespace openvk\Web\Models\Repositories;
|
||||
use openvk\Web\Models\Entities\Club;
|
||||
use openvk\Web\Models\Repositories\Aliases;
|
||||
use openvk\Web\Models\Entities\{Club, Manager};
|
||||
use openvk\Web\Models\Repositories\{Aliases, Users};
|
||||
use Nette\Database\Table\ActiveRow;
|
||||
use Chandler\Database\DatabaseConnection;
|
||||
|
||||
|
@ -9,11 +9,13 @@ class Clubs
|
|||
{
|
||||
private $context;
|
||||
private $clubs;
|
||||
private $coadmins;
|
||||
|
||||
function __construct()
|
||||
{
|
||||
$this->context = DatabaseConnection::i()->getContext();
|
||||
$this->clubs = $this->context->table("groups");
|
||||
$this->context = DatabaseConnection::i()->getContext();
|
||||
$this->clubs = $this->context->table("groups");
|
||||
$this->coadmins = $this->context->table("group_coadmins");
|
||||
}
|
||||
|
||||
private function toClub(?ActiveRow $ar): ?Club
|
||||
|
@ -41,12 +43,12 @@ class Clubs
|
|||
return $this->toClub($this->clubs->get($id));
|
||||
}
|
||||
|
||||
function find(string $query, int $page = 1, ?int $perPage = NULL): \Traversable
|
||||
function find(string $query, array $pars = [], string $sort = "id DESC", int $page = 1, ?int $perPage = NULL): \Traversable
|
||||
{
|
||||
$query = "%$query%";
|
||||
$result = $this->clubs->where("name LIKE ? OR about LIKE ?", $query, $query);
|
||||
|
||||
return new Util\EntityStream("Club", $result);
|
||||
return new Util\EntityStream("Club", $result->order($sort));
|
||||
}
|
||||
|
||||
function getCount(): int
|
||||
|
@ -70,6 +72,26 @@ class Clubs
|
|||
];
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
function getWriteableClubs(int $id): \Traversable
|
||||
{
|
||||
$result = $this->clubs->where("owner", $id);
|
||||
$coadmins = $this->coadmins->where("user", $id);
|
||||
|
||||
foreach($result as $entry) {
|
||||
yield new Club($entry);
|
||||
}
|
||||
|
||||
foreach($coadmins as $coadmin) {
|
||||
$cl = new Manager($coadmin);
|
||||
yield $cl->getClub();
|
||||
}
|
||||
}
|
||||
|
||||
function getWriteableClubsCount(int $id): int
|
||||
{
|
||||
return sizeof($this->clubs->where("owner", $id)) + sizeof($this->coadmins->where("user", $id));
|
||||
}
|
||||
|
||||
use \Nette\SmartObject;
|
||||
}
|
||||
|
|
|
@ -59,4 +59,35 @@ class Comments
|
|||
"deleted" => false,
|
||||
]));
|
||||
}
|
||||
|
||||
function find(string $query = "", array $pars = [], string $sort = "id"): Util\EntityStream
|
||||
{
|
||||
$query = "%$query%";
|
||||
|
||||
$notNullParams = [];
|
||||
|
||||
foreach($pars as $paramName => $paramValue)
|
||||
if($paramName != "before" && $paramName != "after")
|
||||
$paramValue != NULL ? $notNullParams+=["$paramName" => "%$paramValue%"] : NULL;
|
||||
else
|
||||
$paramValue != NULL ? $notNullParams+=["$paramName" => "$paramValue"] : NULL;
|
||||
|
||||
$result = $this->comments->where("content LIKE ?", $query)->where("deleted", 0);
|
||||
$nnparamsCount = sizeof($notNullParams);
|
||||
|
||||
if($nnparamsCount > 0) {
|
||||
foreach($notNullParams as $paramName => $paramValue) {
|
||||
switch($paramName) {
|
||||
case "before":
|
||||
$result->where("created < ?", $paramValue);
|
||||
break;
|
||||
case "after":
|
||||
$result->where("created > ?", $paramValue);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new Util\EntityStream("Comment", $result->order("$sort"));
|
||||
}
|
||||
}
|
||||
|
|
49
Web/Models/Repositories/CurrentUser.php
Normal file
49
Web/Models/Repositories/CurrentUser.php
Normal file
|
@ -0,0 +1,49 @@
|
|||
<?php declare(strict_types=1);
|
||||
namespace openvk\Web\Models\Repositories;
|
||||
use openvk\Web\Models\Entities\User;
|
||||
|
||||
class CurrentUser
|
||||
{
|
||||
private static $instance = null;
|
||||
private $user;
|
||||
private $ip;
|
||||
private $useragent;
|
||||
|
||||
public function __construct(?User $user = NULL, ?string $ip = NULL, ?string $useragent = NULL)
|
||||
{
|
||||
if ($user)
|
||||
$this->user = $user;
|
||||
|
||||
if ($ip)
|
||||
$this->ip = $ip;
|
||||
|
||||
if ($useragent)
|
||||
$this->useragent = $useragent;
|
||||
}
|
||||
|
||||
public static function get($user, $ip, $useragent)
|
||||
{
|
||||
if (self::$instance === null) self::$instance = new self($user, $ip, $useragent);
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
public function getUser(): User
|
||||
{
|
||||
return $this->user;
|
||||
}
|
||||
|
||||
public function getIP(): string
|
||||
{
|
||||
return $this->ip;
|
||||
}
|
||||
|
||||
public function getUserAgent(): string
|
||||
{
|
||||
return $this->useragent;
|
||||
}
|
||||
|
||||
public static function i()
|
||||
{
|
||||
return self::$instance;
|
||||
}
|
||||
}
|
|
@ -42,4 +42,10 @@ class Gifts
|
|||
foreach($cats as $cat)
|
||||
yield new GiftCategory($cat);
|
||||
}
|
||||
|
||||
function getCategoriesCount(): int
|
||||
{
|
||||
$cats = $this->cats->where("deleted", false);
|
||||
return $cats->count();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@ class IPs
|
|||
if(!$res) {
|
||||
$res = new IP;
|
||||
$res->setIp($ip);
|
||||
$res->save();
|
||||
$res->save(false);
|
||||
|
||||
return $res;
|
||||
}
|
||||
|
|
34
Web/Models/Repositories/NoSpamLogs.php
Normal file
34
Web/Models/Repositories/NoSpamLogs.php
Normal file
|
@ -0,0 +1,34 @@
|
|||
<?php declare(strict_types=1);
|
||||
namespace openvk\Web\Models\Repositories;
|
||||
use Chandler\Database\DatabaseConnection;
|
||||
use openvk\Web\Models\Entities\NoSpamLog;
|
||||
use openvk\Web\Models\Entities\User;
|
||||
use Nette\Database\Table\ActiveRow;
|
||||
|
||||
class NoSpamLogs
|
||||
{
|
||||
private $context;
|
||||
private $noSpamLogs;
|
||||
|
||||
function __construct()
|
||||
{
|
||||
$this->context = DatabaseConnection::i()->getContext();
|
||||
$this->noSpamLogs = $this->context->table("noSpam_templates");
|
||||
}
|
||||
|
||||
private function toNoSpamLog(?ActiveRow $ar): ?NoSpamLog
|
||||
{
|
||||
return is_null($ar) ? NULL : new NoSpamLog($ar);
|
||||
}
|
||||
|
||||
function get(int $id): ?NoSpamLog
|
||||
{
|
||||
return $this->toNoSpamLog($this->noSpamLogs->get($id));
|
||||
}
|
||||
|
||||
function getList(array $filter = []): \Traversable
|
||||
{
|
||||
foreach ($this->noSpamLogs->where($filter)->order("`id` DESC") as $log)
|
||||
yield new NoSpamLog($log);
|
||||
}
|
||||
}
|
|
@ -26,10 +26,10 @@ class Notes
|
|||
return $this->toNote($this->notes->get($id));
|
||||
}
|
||||
|
||||
function getUserNotes(User $user, int $page = 1, ?int $perPage = NULL): \Traversable
|
||||
function getUserNotes(User $user, int $page = 1, ?int $perPage = NULL, string $sort = "DESC"): \Traversable
|
||||
{
|
||||
$perPage = $perPage ?? OPENVK_DEFAULT_PER_PAGE;
|
||||
foreach($this->notes->where("owner", $user->getId())->where("deleted", 0)->order("created DESC")->page($page, $perPage) as $album)
|
||||
foreach($this->notes->where("owner", $user->getId())->where("deleted", 0)->order("created $sort")->page($page, $perPage) as $album)
|
||||
yield new Note($album);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<?php declare(strict_types=1);
|
||||
namespace openvk\Web\Models\Repositories;
|
||||
use openvk\Web\Models\Entities\Photo;
|
||||
use openvk\Web\Models\Entities\{Photo, User};
|
||||
use Chandler\Database\DatabaseConnection;
|
||||
|
||||
class Photos
|
||||
|
@ -32,4 +32,27 @@ class Photos
|
|||
|
||||
return new Photo($photo);
|
||||
}
|
||||
|
||||
function getEveryUserPhoto(User $user, int $page = 1, ?int $perPage = NULL): \Traversable
|
||||
{
|
||||
$perPage = $perPage ?? OPENVK_DEFAULT_PER_PAGE;
|
||||
$photos = $this->photos->where([
|
||||
"owner" => $user->getId(),
|
||||
"deleted" => 0
|
||||
])->order("id DESC");
|
||||
|
||||
foreach($photos->page($page, $perPage) as $photo) {
|
||||
yield new Photo($photo);
|
||||
}
|
||||
}
|
||||
|
||||
function getUserPhotosCount(User $user)
|
||||
{
|
||||
$photos = $this->photos->where([
|
||||
"owner" => $user->getId(),
|
||||
"deleted" => 0
|
||||
]);
|
||||
|
||||
return sizeof($photos);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -99,7 +99,39 @@ class Posts
|
|||
return NULL;
|
||||
|
||||
}
|
||||
|
||||
|
||||
function find(string $query = "", array $pars = [], string $sort = "id"): Util\EntityStream
|
||||
{
|
||||
$query = "%$query%";
|
||||
|
||||
$notNullParams = [];
|
||||
|
||||
foreach($pars as $paramName => $paramValue)
|
||||
if($paramName != "before" && $paramName != "after")
|
||||
$paramValue != NULL ? $notNullParams+=["$paramName" => "%$paramValue%"] : NULL;
|
||||
else
|
||||
$paramValue != NULL ? $notNullParams+=["$paramName" => "$paramValue"] : NULL;
|
||||
|
||||
$result = $this->posts->where("content LIKE ?", $query)->where("deleted", 0);
|
||||
$nnparamsCount = sizeof($notNullParams);
|
||||
|
||||
if($nnparamsCount > 0) {
|
||||
foreach($notNullParams as $paramName => $paramValue) {
|
||||
switch($paramName) {
|
||||
case "before":
|
||||
$result->where("created < ?", $paramValue);
|
||||
break;
|
||||
case "after":
|
||||
$result->where("created > ?", $paramValue);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return new Util\EntityStream("Post", $result->order("$sort"));
|
||||
}
|
||||
|
||||
function getPostCountOnUserWall(int $user): int
|
||||
{
|
||||
return sizeof($this->posts->where(["wall" => $user, "deleted" => 0]));
|
||||
|
|
67
Web/Models/Repositories/Reports.php
Normal file
67
Web/Models/Repositories/Reports.php
Normal file
|
@ -0,0 +1,67 @@
|
|||
<?php declare(strict_types=1);
|
||||
namespace openvk\Web\Models\Repositories;
|
||||
use openvk\Web\Models\Entities\Report;
|
||||
use Nette\Database\Table\ActiveRow;
|
||||
use Chandler\Database\DatabaseConnection;
|
||||
|
||||
class Reports
|
||||
{
|
||||
private $context;
|
||||
private $reports;
|
||||
|
||||
function __construct()
|
||||
{
|
||||
$this->context = DatabaseConnection::i()->getContext();
|
||||
$this->reports = $this->context->table("reports");
|
||||
}
|
||||
|
||||
private function toReport(?ActiveRow $ar): ?Report
|
||||
{
|
||||
return is_null($ar) ? NULL : new Report($ar);
|
||||
}
|
||||
|
||||
function getReports(int $state = 0, int $page = 1, ?string $type = NULL, ?bool $pagination = true): \Traversable
|
||||
{
|
||||
$filter = ["deleted" => 0];
|
||||
if ($type) $filter["type"] = $type;
|
||||
|
||||
$reports = $this->reports->where($filter)->order("created DESC")->group("target_id, type");
|
||||
if ($pagination)
|
||||
$reports = $reports->page($page, 15);
|
||||
|
||||
foreach($reports as $t)
|
||||
yield new Report($t);
|
||||
}
|
||||
|
||||
function getReportsCount(int $state = 0): int
|
||||
{
|
||||
return sizeof($this->reports->where(["deleted" => 0, "type" => $state])->group("target_id, type"));
|
||||
}
|
||||
|
||||
function get(int $id): ?Report
|
||||
{
|
||||
return $this->toReport($this->reports->get($id));
|
||||
}
|
||||
|
||||
function getByContentId(int $id): ?Report
|
||||
{
|
||||
$post = $this->reports->where(["deleted" => 0, "content_id" => $id])->fetch();
|
||||
|
||||
if($post)
|
||||
return new Report($post);
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
function getDuplicates(string $type, int $target_id, ?int $orig = NULL, ?int $user_id = NULL): \Traversable
|
||||
{
|
||||
$filter = ["deleted" => 0, "type" => $type, "target_id" => $target_id];
|
||||
if ($orig) $filter[] = "id != $orig";
|
||||
if ($user_id) $filter["user_id"] = $user_id;
|
||||
|
||||
foreach ($this->reports->where($filter) as $report)
|
||||
yield new Report($report);
|
||||
}
|
||||
|
||||
use \Nette\SmartObject;
|
||||
}
|
|
@ -44,17 +44,96 @@ class Users
|
|||
return $alias->getUser();
|
||||
}
|
||||
|
||||
function getByChandlerUser(ChandlerUser $user): ?User
|
||||
function getByChandlerUser(?ChandlerUser $user): ?User
|
||||
{
|
||||
return $this->toUser($this->users->where("user", $user->getId())->fetch());
|
||||
return $user ? $this->toUser($this->users->where("user", $user->getId())->fetch()) : NULL;
|
||||
}
|
||||
|
||||
function find(string $query): Util\EntityStream
|
||||
function find(string $query, array $pars = [], string $sort = "id DESC"): Util\EntityStream
|
||||
{
|
||||
$query = "%$query%";
|
||||
$result = $this->users->where("CONCAT_WS(' ', first_name, last_name, pseudo, shortcode) LIKE ?", $query)->where("deleted", 0);
|
||||
|
||||
return new Util\EntityStream("User", $result);
|
||||
$notNullParams = [];
|
||||
$nnparamsCount = 0;
|
||||
|
||||
foreach($pars as $paramName => $paramValue)
|
||||
if($paramName != "before" && $paramName != "after" && $paramName != "gender" && $paramName != "maritalstatus" && $paramName != "politViews" && $paramName != "doNotSearchMe")
|
||||
$paramValue != NULL ? $notNullParams += ["$paramName" => "%$paramValue%"] : NULL;
|
||||
else
|
||||
$paramValue != NULL ? $notNullParams += ["$paramName" => "$paramValue"] : NULL;
|
||||
|
||||
$nnparamsCount = sizeof($notNullParams);
|
||||
|
||||
if($nnparamsCount > 0) {
|
||||
foreach($notNullParams as $paramName => $paramValue) {
|
||||
switch($paramName) {
|
||||
case "hometown":
|
||||
$result->where("hometown LIKE ?", $paramValue);
|
||||
break;
|
||||
case "city":
|
||||
$result->where("city LIKE ?", $paramValue);
|
||||
break;
|
||||
case "maritalstatus":
|
||||
$result->where("marital_status ?", $paramValue);
|
||||
break;
|
||||
case "status":
|
||||
$result->where("status LIKE ?", $paramValue);
|
||||
break;
|
||||
case "politViews":
|
||||
$result->where("polit_views ?", $paramValue);
|
||||
break;
|
||||
case "email":
|
||||
$result->where("email_contact LIKE ?", $paramValue);
|
||||
break;
|
||||
case "telegram":
|
||||
$result->where("telegram LIKE ?", $paramValue);
|
||||
break;
|
||||
case "site":
|
||||
$result->where("telegram LIKE ?", $paramValue);
|
||||
break;
|
||||
case "address":
|
||||
$result->where("address LIKE ?", $paramValue);
|
||||
break;
|
||||
case "is_online":
|
||||
$result->where("online >= ?", time() - 900);
|
||||
break;
|
||||
case "interests":
|
||||
$result->where("interests LIKE ?", $paramValue);
|
||||
break;
|
||||
case "fav_mus":
|
||||
$result->where("fav_music LIKE ?", $paramValue);
|
||||
break;
|
||||
case "fav_films":
|
||||
$result->where("fav_films LIKE ?", $paramValue);
|
||||
break;
|
||||
case "fav_shows":
|
||||
$result->where("fav_shows LIKE ?", $paramValue);
|
||||
break;
|
||||
case "fav_books":
|
||||
$result->where("fav_books LIKE ?", $paramValue);
|
||||
break;
|
||||
case "fav_quote":
|
||||
$result->where("fav_quote LIKE ?", $paramValue);
|
||||
break;
|
||||
case "before":
|
||||
$result->where("UNIX_TIMESTAMP(since) < ?", $paramValue);
|
||||
break;
|
||||
case "after":
|
||||
$result->where("UNIX_TIMESTAMP(since) > ?", $paramValue);
|
||||
break;
|
||||
case "gender":
|
||||
$result->where("sex ?", $paramValue);
|
||||
break;
|
||||
case "doNotSearchMe":
|
||||
$result->where("id !=", $paramValue);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return new Util\EntityStream("User", $result->order($sort));
|
||||
}
|
||||
|
||||
function getStatistics(): object
|
||||
|
|
|
@ -45,4 +45,36 @@ class Videos
|
|||
{
|
||||
return sizeof($this->videos->where("owner", $user->getId())->where(["deleted" => 0, "unlisted" => 0]));
|
||||
}
|
||||
|
||||
function find(string $query = "", array $pars = [], string $sort = "id"): Util\EntityStream
|
||||
{
|
||||
$query = "%$query%";
|
||||
|
||||
$notNullParams = [];
|
||||
|
||||
foreach($pars as $paramName => $paramValue)
|
||||
if($paramName != "before" && $paramName != "after")
|
||||
$paramValue != NULL ? $notNullParams+=["$paramName" => "%$paramValue%"] : NULL;
|
||||
else
|
||||
$paramValue != NULL ? $notNullParams+=["$paramName" => "$paramValue"] : NULL;
|
||||
|
||||
$result = $this->videos->where("CONCAT_WS(' ', name, description) LIKE ?", $query)->where("deleted", 0);
|
||||
$nnparamsCount = sizeof($notNullParams);
|
||||
|
||||
if($nnparamsCount > 0) {
|
||||
foreach($notNullParams as $paramName => $paramValue) {
|
||||
switch($paramName) {
|
||||
case "before":
|
||||
$result->where("created < ?", $paramValue);
|
||||
break;
|
||||
case "after":
|
||||
$result->where("created > ?", $paramValue);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return new Util\EntityStream("Video", $result->order("$sort"));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,5 +14,5 @@ abstract class VideoDriver
|
|||
|
||||
abstract function getURL(): string;
|
||||
|
||||
abstract function getEmbed(): string;
|
||||
abstract function getEmbed(string $w = "600", string $h = "340"): string;
|
||||
}
|
||||
|
|
|
@ -13,12 +13,12 @@ final class YouTubeVideoDriver extends VideoDriver
|
|||
return "https://youtu.be/$this->id";
|
||||
}
|
||||
|
||||
function getEmbed(): string
|
||||
function getEmbed(string $w = "600", string $h = "340"): string
|
||||
{
|
||||
return <<<CODE
|
||||
<iframe
|
||||
width="600"
|
||||
height="340"
|
||||
width="$w"
|
||||
height="$h"
|
||||
src="https://www.youtube-nocookie.com/embed/$this->id"
|
||||
frameborder="0"
|
||||
sandbox="allow-same-origin allow-scripts allow-popups"
|
||||
|
|
|
@ -13,8 +13,8 @@ Move-Item $file $temp
|
|||
|
||||
# video stub logic was implicitly deprecated, so we start processing at once
|
||||
ffmpeg -i $temp -ss 00:00:01.000 -vframes 1 "$dir$hashT/$hash.gif"
|
||||
ffmpeg -i $temp -c:v libtheora -q:v 7 -c:a libvorbis -q:a 4 -vf "scale=640:480:force_original_aspect_ratio=decrease,pad=640:480:(ow-iw)/2:(oh-ih)/2,setsar=1" -y $temp2
|
||||
ffmpeg -i $temp -c:v libx264 -q:v 7 -c:a libmp3lame -q:a 4 -tune zerolatency -vf "scale=640:480:force_original_aspect_ratio=decrease,pad=640:480:(ow-iw)/2:(oh-ih)/2,setsar=1" -y $temp2
|
||||
|
||||
Move-Item $temp2 "$dir$hashT/$hash.ogv"
|
||||
Move-Item $temp2 "$dir$hashT/$hash.mp4"
|
||||
Remove-Item $temp
|
||||
Remove-Item $temp2
|
||||
|
|
|
@ -1,14 +1,12 @@
|
|||
tmpfile="$RANDOM-$(date +%s%N)"
|
||||
|
||||
cp $2 "/tmp/vid_$tmpfile.bin"
|
||||
cp ../files/video/rendering.apng $3${4:0:2}/$4.gif
|
||||
cp ../files/video/rendering.ogv $3/${4:0:2}/$4.ogv
|
||||
|
||||
nice ffmpeg -i "/tmp/vid_$tmpfile.bin" -ss 00:00:01.000 -vframes 1 $3${4:0:2}/$4.gif
|
||||
nice -n 20 ffmpeg -i "/tmp/vid_$tmpfile.bin" -c:v libtheora -q:v 7 -c:a libvorbis -q:a 4 -vf "scale=640:480:force_original_aspect_ratio=decrease,pad=640:480:(ow-iw)/2:(oh-ih)/2,setsar=1" -y "/tmp/ffmOi$tmpfile.ogv"
|
||||
nice -n 20 ffmpeg -i "/tmp/vid_$tmpfile.bin" -c:v libx264 -q:v 7 -c:a libmp3lame -q:a 4 -tune zerolatency -vf "scale=640:480:force_original_aspect_ratio=decrease,pad=640:480:(ow-iw)/2:(oh-ih)/2,setsar=1" -y "/tmp/ffmOi$tmpfile.mp4"
|
||||
|
||||
rm -rf $3${4:0:2}/$4.ogv
|
||||
mv "/tmp/ffmOi$tmpfile.ogv" $3${4:0:2}/$4.ogv
|
||||
rm -rf $3${4:0:2}/$4.mp4
|
||||
mv "/tmp/ffmOi$tmpfile.mp4" $3${4:0:2}/$4.mp4
|
||||
|
||||
rm -f "/tmp/ffmOi$tmpfile.ogv"
|
||||
rm -f "/tmp/ffmOi$tmpfile.mp4"
|
||||
rm -f "/tmp/vid_$tmpfile.bin"
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
(SELECT follower AS __id FROM
|
||||
(SELECT DISTINCT(follower) AS __id FROM
|
||||
(SELECT follower FROM subscriptions WHERE target=? AND model="openvk\\Web\\Models\\Entities\\User") u0
|
||||
LEFT JOIN
|
||||
(SELECT target FROM subscriptions WHERE follower=? AND model="openvk\\Web\\Models\\Entities\\User") u1
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
(SELECT follower AS __id FROM
|
||||
(SELECT DISTINCT(follower) AS __id FROM
|
||||
(SELECT follower FROM subscriptions WHERE target=? AND model="openvk\\Web\\Models\\Entities\\User") u0
|
||||
INNER JOIN
|
||||
(SELECT target FROM subscriptions WHERE follower=? AND model="openvk\\Web\\Models\\Entities\\User") u1
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
(SELECT follower AS __id FROM
|
||||
(SELECT DISTINCT(follower) AS __id FROM
|
||||
(SELECT follower FROM subscriptions WHERE target=? AND model="openvk\\Web\\Models\\Entities\\User") u0
|
||||
INNER JOIN
|
||||
(SELECT target FROM subscriptions WHERE follower=? AND model="openvk\\Web\\Models\\Entities\\User") u1
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
(SELECT target AS __id FROM
|
||||
(SELECT DISTINCT(target) AS __id FROM
|
||||
(SELECT follower FROM subscriptions WHERE target=? AND model="openvk\\Web\\Models\\Entities\\User") u0
|
||||
RIGHT JOIN
|
||||
(SELECT target FROM subscriptions WHERE follower=? AND model="openvk\\Web\\Models\\Entities\\User") u1
|
||||
|
|
|
@ -37,6 +37,9 @@ final class AboutPresenter extends OpenVKPresenter
|
|||
|
||||
function renderBB(): void
|
||||
{}
|
||||
|
||||
function renderTour(): void
|
||||
{}
|
||||
|
||||
function renderInvite(): void
|
||||
{
|
||||
|
@ -138,6 +141,6 @@ final class AboutPresenter extends OpenVKPresenter
|
|||
|
||||
function renderDev(): void
|
||||
{
|
||||
$this->redirect("https://docs.openvk.su/");
|
||||
$this->redirect("https://docs.openvk.uk/");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,11 +1,17 @@
|
|||
<?php declare(strict_types=1);
|
||||
namespace openvk\Web\Presenters;
|
||||
use Chandler\Database\Log;
|
||||
use Chandler\Database\Logs;
|
||||
use openvk\Web\Models\Entities\{Voucher, Gift, GiftCategory, User, BannedLink};
|
||||
use openvk\Web\Models\Repositories\{Audios,
|
||||
Bans,
|
||||
ChandlerGroups,
|
||||
ChandlerUsers,
|
||||
Photos,
|
||||
Posts,
|
||||
Users,
|
||||
Clubs,
|
||||
Videos,
|
||||
Util\EntityStream,
|
||||
Vouchers,
|
||||
Gifts,
|
||||
|
@ -21,6 +27,7 @@ final class AdminPresenter extends OpenVKPresenter
|
|||
private $bannedLinks;
|
||||
private $chandlerGroups;
|
||||
private $audios;
|
||||
private $logs;
|
||||
|
||||
function __construct(Users $users, Clubs $clubs, Vouchers $vouchers, Gifts $gifts, BannedLinks $bannedLinks, ChandlerGroups $chandlerGroups, Audios $audios)
|
||||
{
|
||||
|
@ -31,7 +38,8 @@ final class AdminPresenter extends OpenVKPresenter
|
|||
$this->bannedLinks = $bannedLinks;
|
||||
$this->chandlerGroups = $chandlerGroups;
|
||||
$this->audios = $audios;
|
||||
|
||||
$this->logs = DatabaseConnection::i()->getContext()->table("ChandlerLogs");
|
||||
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
|
@ -105,6 +113,9 @@ final class AdminPresenter extends OpenVKPresenter
|
|||
$query = "INSERT INTO `ChandlerACLRelations` (`user`, `group`) VALUES ('" . $user->getChandlerGUID() . "', '" . $this->postParam("add-to-group") . "')";
|
||||
DatabaseConnection::i()->getConnection()->query($query);
|
||||
}
|
||||
if($this->postParam("password")) {
|
||||
$user->getChandlerUser()->updatePassword($this->postParam("password"));
|
||||
}
|
||||
|
||||
$user->save();
|
||||
|
||||
|
@ -144,7 +155,8 @@ final class AdminPresenter extends OpenVKPresenter
|
|||
$club->save();
|
||||
break;
|
||||
case "ban":
|
||||
$club->setBlock_reason($this->postParam("ban_reason"));
|
||||
$reason = mb_strlen(trim($this->postParam("ban_reason"))) > 0 ? $this->postParam("ban_reason") : NULL;
|
||||
$club->setBlock_reason($reason);
|
||||
$club->save();
|
||||
break;
|
||||
}
|
||||
|
@ -294,7 +306,7 @@ final class AdminPresenter extends OpenVKPresenter
|
|||
$this->notFound();
|
||||
|
||||
$gift->delete();
|
||||
$this->flashFail("succ", "Gift moved successfully", "This gift will now be in <b>Recycle Bin</b>.");
|
||||
$this->flashFail("succ", tr("admin_gift_moved_successfully"), tr("admin_gift_moved_to_recycle"));
|
||||
break;
|
||||
case "copy":
|
||||
case "move":
|
||||
|
@ -313,7 +325,7 @@ final class AdminPresenter extends OpenVKPresenter
|
|||
$catTo->addGift($gift);
|
||||
|
||||
$name = $catTo->getName();
|
||||
$this->flash("succ", "Gift moved successfully", "This gift will now be in <b>$name</b>.");
|
||||
$this->flash("succ", tr("admin_gift_moved_successfully"), "This gift will now be in <b>$name</b>.");
|
||||
$this->redirect("/admin/gifts/" . $catTo->getSlug() . "." . $catTo->getId() . "/");
|
||||
break;
|
||||
default:
|
||||
|
@ -344,10 +356,10 @@ final class AdminPresenter extends OpenVKPresenter
|
|||
$gift->setUsages((int) $this->postParam("usages"));
|
||||
if(isset($_FILES["pic"]) && $_FILES["pic"]["error"] === UPLOAD_ERR_OK) {
|
||||
if(!$gift->setImage($_FILES["pic"]["tmp_name"]))
|
||||
$this->flashFail("err", "Не удалось сохранить подарок", "Изображение подарка кривое.");
|
||||
$this->flashFail("err", tr("error_when_saving_gift"), tr("error_when_saving_gift_bad_image"));
|
||||
} else if($gen) {
|
||||
# If there's no gift pic but it's newly created
|
||||
$this->flashFail("err", "Не удалось сохранить подарок", "Пожалуйста, загрузите изображение подарка.");
|
||||
$this->flashFail("err", tr("error_when_saving_gift"), tr("error_when_saving_gift_no_image"));
|
||||
}
|
||||
|
||||
$gift->save();
|
||||
|
@ -371,13 +383,19 @@ final class AdminPresenter extends OpenVKPresenter
|
|||
{
|
||||
$this->assertNoCSRF();
|
||||
|
||||
$unban_time = strtotime($this->queryParam("date")) ?: NULL;
|
||||
if (str_contains($this->queryParam("reason"), "*"))
|
||||
exit(json_encode([ "error" => "Incorrect reason" ]));
|
||||
|
||||
$unban_time = strtotime($this->queryParam("date")) ?: "permanent";
|
||||
|
||||
$user = $this->users->get($id);
|
||||
if(!$user)
|
||||
exit(json_encode([ "error" => "User does not exist" ]));
|
||||
|
||||
$user->ban($this->queryParam("reason"), true, $unban_time);
|
||||
|
||||
if ($this->queryParam("incr"))
|
||||
$unban_time = time() + $user->getNewBanTime();
|
||||
|
||||
$user->ban($this->queryParam("reason"), true, $unban_time, $this->user->identity->getId());
|
||||
exit(json_encode([ "success" => true, "reason" => $this->queryParam("reason") ]));
|
||||
}
|
||||
|
||||
|
@ -388,9 +406,17 @@ final class AdminPresenter extends OpenVKPresenter
|
|||
$user = $this->users->get($id);
|
||||
if(!$user)
|
||||
exit(json_encode([ "error" => "User does not exist" ]));
|
||||
|
||||
|
||||
$ban = (new Bans)->get((int)$user->getRawBanReason());
|
||||
if (!$ban || $ban->isOver())
|
||||
exit(json_encode([ "error" => "User is not banned" ]));
|
||||
|
||||
$ban->setRemoved_Manually(true);
|
||||
$ban->setRemoved_By($this->user->identity->getId());
|
||||
$ban->save();
|
||||
|
||||
$user->setBlock_Reason(NULL);
|
||||
$user->setUnblock_time(NULL);
|
||||
// $user->setUnblock_time(NULL);
|
||||
$user->save();
|
||||
exit(json_encode([ "success" => true ]));
|
||||
}
|
||||
|
@ -476,6 +502,14 @@ final class AdminPresenter extends OpenVKPresenter
|
|||
$this->redirect("/admin/bannedLinks");
|
||||
}
|
||||
|
||||
function renderBansHistory(int $user_id) :void
|
||||
{
|
||||
$user = (new Users)->get($user_id);
|
||||
if (!$user) $this->notFound();
|
||||
|
||||
$this->template->bans = (new Bans)->getByUser($user_id);
|
||||
}
|
||||
|
||||
function renderChandlerGroups(): void
|
||||
{
|
||||
$this->template->groups = (new ChandlerGroups)->getList();
|
||||
|
@ -608,4 +642,38 @@ final class AdminPresenter extends OpenVKPresenter
|
|||
$playlist->save();
|
||||
}
|
||||
}
|
||||
|
||||
function renderLogs(): void
|
||||
{
|
||||
$filter = [];
|
||||
|
||||
if ($this->queryParam("id")) {
|
||||
$id = (int) $this->queryParam("id");
|
||||
$filter["id"] = $id;
|
||||
$this->template->id = $id;
|
||||
}
|
||||
if ($this->queryParam("type") !== NULL && $this->queryParam("type") !== "any") {
|
||||
$type = in_array($this->queryParam("type"), [0, 1, 2, 3]) ? (int) $this->queryParam("type") : 0;
|
||||
$filter["type"] = $type;
|
||||
$this->template->type = $type;
|
||||
}
|
||||
if ($this->queryParam("uid")) {
|
||||
$user = $this->queryParam("uid");
|
||||
$filter["user"] = $user;
|
||||
$this->template->user = $user;
|
||||
}
|
||||
if ($this->queryParam("obj_id")) {
|
||||
$obj_id = (int) $this->queryParam("obj_id");
|
||||
$filter["object_id"] = $obj_id;
|
||||
$this->template->obj_id = $obj_id;
|
||||
}
|
||||
if ($this->queryParam("obj_type") !== NULL && $this->queryParam("obj_type") !== "any") {
|
||||
$obj_type = "openvk\\Web\\Models\\Entities\\" . $this->queryParam("obj_type");
|
||||
$filter["object_model"] = $obj_type;
|
||||
$this->template->obj_type = $obj_type;
|
||||
}
|
||||
|
||||
$this->template->logs = (new Logs)->search($filter);
|
||||
$this->template->object_types = (new Logs)->getTypes();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<?php declare(strict_types=1);
|
||||
namespace openvk\Web\Presenters;
|
||||
use openvk\Web\Models\Entities\{IP, User, PasswordReset, EmailVerification};
|
||||
use openvk\Web\Models\Repositories\{IPs, Users, Restores, Verifications};
|
||||
use openvk\Web\Models\Repositories\{Bans, IPs, Users, Restores, Verifications};
|
||||
use openvk\Web\Models\Exceptions\InvalidUserNameException;
|
||||
use openvk\Web\Util\Validator;
|
||||
use Chandler\Session\Session;
|
||||
|
@ -80,7 +80,11 @@ final class AuthPresenter extends OpenVKPresenter
|
|||
|
||||
if(!Validator::i()->emailValid($this->postParam("email")))
|
||||
$this->flashFail("err", tr("invalid_email_address"), tr("invalid_email_address_comment"));
|
||||
|
||||
|
||||
if(OPENVK_ROOT_CONF['openvk']['preferences']['security']['forceStrongPassword'])
|
||||
if(!Validator::i()->passwordStrong($this->postParam("password")))
|
||||
$this->flashFail("err", tr("error"), tr("error_weak_password"));
|
||||
|
||||
if (strtotime($this->postParam("birthday")) > time())
|
||||
$this->flashFail("err", tr("invalid_birth_date"), tr("invalid_birth_date_comment"));
|
||||
|
||||
|
@ -106,7 +110,7 @@ final class AuthPresenter extends OpenVKPresenter
|
|||
$this->flashFail("err", tr("failed_to_register"), tr("user_already_exists"));
|
||||
|
||||
$user->setUser($chUser->getId());
|
||||
$user->save();
|
||||
$user->save(false);
|
||||
|
||||
if(!is_null($referer)) {
|
||||
$user->toggleSubscription($referer);
|
||||
|
@ -127,6 +131,7 @@ final class AuthPresenter extends OpenVKPresenter
|
|||
|
||||
$this->authenticator->authenticate($chUser->getId());
|
||||
$this->redirect("/id" . $user->getId());
|
||||
$user->save();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -203,6 +208,9 @@ final class AuthPresenter extends OpenVKPresenter
|
|||
|
||||
function renderFinishRestoringPassword(): void
|
||||
{
|
||||
if(OPENVK_ROOT_CONF['openvk']['preferences']['security']['disablePasswordRestoring'])
|
||||
$this->notFound();
|
||||
|
||||
$request = $this->restores->getByToken(str_replace(" ", "+", $this->queryParam("key")));
|
||||
if(!$request || !$request->isStillValid()) {
|
||||
$this->flash("err", tr("token_manipulation_error"), tr("token_manipulation_error_comment"));
|
||||
|
@ -237,6 +245,9 @@ final class AuthPresenter extends OpenVKPresenter
|
|||
|
||||
function renderRestore(): void
|
||||
{
|
||||
if(OPENVK_ROOT_CONF['openvk']['preferences']['security']['disablePasswordRestoring'])
|
||||
$this->notFound();
|
||||
|
||||
if(!is_null($this->user))
|
||||
$this->redirect($this->user->identity->getURL());
|
||||
|
||||
|
@ -335,9 +346,16 @@ final class AuthPresenter extends OpenVKPresenter
|
|||
$this->flashFail("err", tr("error"), tr("forbidden"));
|
||||
|
||||
$user = $this->users->get($this->user->id);
|
||||
$ban = (new Bans)->get((int)$user->getRawBanReason());
|
||||
if (!$ban || $ban->isOver() || $ban->isPermanent())
|
||||
$this->flashFail("err", tr("error"), tr("forbidden"));
|
||||
|
||||
$ban->setRemoved_Manually(2);
|
||||
$ban->setRemoved_By($this->user->identity->getId());
|
||||
$ban->save();
|
||||
|
||||
$user->setBlock_Reason(NULL);
|
||||
$user->setUnblock_Time(NULL);
|
||||
// $user->setUnblock_Time(NULL);
|
||||
$user->save();
|
||||
|
||||
$this->flashFail("succ", tr("banned_unban_title"), tr("banned_unban_description"));
|
||||
|
|
|
@ -3,6 +3,8 @@ namespace openvk\Web\Presenters;
|
|||
|
||||
final class BlobPresenter extends OpenVKPresenter
|
||||
{
|
||||
protected $banTolerant = true;
|
||||
|
||||
private function getDirName($dir): string
|
||||
{
|
||||
if(gettype($dir) === "integer") {
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
namespace openvk\Web\Presenters;
|
||||
use openvk\Web\Models\Entities\{Comment, Notifications\MentionNotification, Photo, Video, User, Topic, Post};
|
||||
use openvk\Web\Models\Entities\Notifications\CommentNotification;
|
||||
use openvk\Web\Models\Repositories\{Comments, Clubs};
|
||||
use openvk\Web\Models\Repositories\{Comments, Clubs, Videos, Photos};
|
||||
|
||||
final class CommentPresenter extends OpenVKPresenter
|
||||
{
|
||||
|
@ -22,6 +22,9 @@ final class CommentPresenter extends OpenVKPresenter
|
|||
|
||||
$comment = (new Comments)->get($id);
|
||||
if(!$comment || $comment->isDeleted()) $this->notFound();
|
||||
|
||||
if ($comment->getTarget() instanceof Post && $comment->getTarget()->getWallOwner()->isBanned())
|
||||
$this->flashFail("err", tr("error"), tr("forbidden"));
|
||||
|
||||
if(!is_null($this->user)) $comment->toggleLike($this->user->identity);
|
||||
|
||||
|
@ -47,7 +50,10 @@ final class CommentPresenter extends OpenVKPresenter
|
|||
$club = (new Clubs)->get(abs($entity->getTargetWall()));
|
||||
else if($entity instanceof Topic)
|
||||
$club = $entity->getClub();
|
||||
|
||||
|
||||
if ($entity instanceof Post && $entity->getWallOwner()->isBanned())
|
||||
$this->flashFail("err", tr("error"), tr("forbidden"));
|
||||
|
||||
$flags = 0;
|
||||
if($this->postParam("as_group") === "on" && !is_null($club) && $club->canBeModifiedBy($this->user->identity))
|
||||
$flags |= 0b10000000;
|
||||
|
@ -57,31 +63,49 @@ final class CommentPresenter extends OpenVKPresenter
|
|||
try {
|
||||
$photo = Photo::fastMake($this->user->id, $this->postParam("text"), $_FILES["_pic_attachment"]);
|
||||
} catch(ISE $ex) {
|
||||
$this->flashFail("err", "Не удалось опубликовать пост", "Файл изображения повреждён, слишком велик или одна сторона изображения в разы больше другой.");
|
||||
$this->flashFail("err", tr("error_when_publishing_comment"), tr("error_when_publishing_comment_description"));
|
||||
}
|
||||
}
|
||||
|
||||
# TODO move to trait
|
||||
try {
|
||||
$photo = NULL;
|
||||
$video = NULL;
|
||||
if($_FILES["_pic_attachment"]["error"] === UPLOAD_ERR_OK) {
|
||||
$album = NULL;
|
||||
if($wall > 0 && $wall === $this->user->id)
|
||||
$album = (new Albums)->getUserWallAlbum($wallOwner);
|
||||
|
||||
$photo = Photo::fastMake($this->user->id, $this->postParam("text"), $_FILES["_pic_attachment"], $album);
|
||||
$photos = [];
|
||||
if(!empty($this->postParam("photos"))) {
|
||||
$un = rtrim($this->postParam("photos"), ",");
|
||||
$arr = explode(",", $un);
|
||||
|
||||
if(sizeof($arr) < 11) {
|
||||
foreach($arr as $dat) {
|
||||
$ids = explode("_", $dat);
|
||||
$photo = (new Photos)->getByOwnerAndVID((int)$ids[0], (int)$ids[1]);
|
||||
|
||||
if(!$photo || $photo->isDeleted())
|
||||
continue;
|
||||
|
||||
$photos[] = $photo;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$videos = [];
|
||||
|
||||
if(!empty($this->postParam("videos"))) {
|
||||
$un = rtrim($this->postParam("videos"), ",");
|
||||
$arr = explode(",", $un);
|
||||
|
||||
if($_FILES["_vid_attachment"]["error"] === UPLOAD_ERR_OK) {
|
||||
$video = Video::fastMake($this->user->id, $this->postParam("text"), $_FILES["_vid_attachment"]);
|
||||
if(sizeof($arr) < 11) {
|
||||
foreach($arr as $dat) {
|
||||
$ids = explode("_", $dat);
|
||||
$video = (new Videos)->getByOwnerAndVID((int)$ids[0], (int)$ids[1]);
|
||||
|
||||
if(!$video || $video->isDeleted())
|
||||
continue;
|
||||
|
||||
$videos[] = $video;
|
||||
}
|
||||
}
|
||||
} catch(ISE $ex) {
|
||||
$this->flashFail("err", "Не удалось опубликовать комментарий", "Файл медиаконтента повреждён или слишком велик.");
|
||||
}
|
||||
|
||||
if(empty($this->postParam("text")) && !$photo && !$video)
|
||||
$this->flashFail("err", "Не удалось опубликовать комментарий", "Комментарий пустой или слишком большой.");
|
||||
if(empty($this->postParam("text")) && sizeof($photos) < 1 && sizeof($videos) < 1)
|
||||
$this->flashFail("err", tr("error_when_publishing_comment"), tr("error_comment_empty"));
|
||||
|
||||
try {
|
||||
$comment = new Comment;
|
||||
|
@ -93,14 +117,15 @@ final class CommentPresenter extends OpenVKPresenter
|
|||
$comment->setFlags($flags);
|
||||
$comment->save();
|
||||
} catch (\LengthException $ex) {
|
||||
$this->flashFail("err", "Не удалось опубликовать комментарий", "Комментарий слишком большой.");
|
||||
$this->flashFail("err", tr("error_when_publishing_comment"), tr("error_comment_too_big"));
|
||||
}
|
||||
|
||||
if(!is_null($photo))
|
||||
$comment->attach($photo);
|
||||
foreach($photos as $photo)
|
||||
$comment->attach($photo);
|
||||
|
||||
if(!is_null($video))
|
||||
$comment->attach($video);
|
||||
if(sizeof($videos) > 0)
|
||||
foreach($videos as $vid)
|
||||
$comment->attach($vid);
|
||||
|
||||
if($entity->getOwner()->getId() !== $this->user->identity->getId())
|
||||
if(($owner = $entity->getOwner()) instanceof User)
|
||||
|
@ -115,7 +140,7 @@ final class CommentPresenter extends OpenVKPresenter
|
|||
if($mentionee instanceof User)
|
||||
(new MentionNotification($mentionee, $entity, $comment->getOwner(), strip_tags($comment->getText())))->emit();
|
||||
|
||||
$this->flashFail("succ", "Комментарий добавлен", "Ваш комментарий появится на странице.");
|
||||
$this->flashFail("succ", tr("comment_is_added"), tr("comment_is_added_desc"));
|
||||
}
|
||||
|
||||
function renderDeleteComment(int $id): void
|
||||
|
@ -126,13 +151,15 @@ final class CommentPresenter extends OpenVKPresenter
|
|||
$comment = (new Comments)->get($id);
|
||||
if(!$comment) $this->notFound();
|
||||
if(!$comment->canBeDeletedBy($this->user->identity))
|
||||
$this->throwError(403, "Forbidden", "У вас недостаточно прав чтобы редактировать этот ресурс.");
|
||||
|
||||
$this->throwError(403, "Forbidden", tr("error_access_denied"));
|
||||
if ($comment->getTarget() instanceof Post && $comment->getTarget()->getWallOwner()->isBanned())
|
||||
$this->flashFail("err", tr("error"), tr("forbidden"));
|
||||
|
||||
$comment->delete();
|
||||
$this->flashFail(
|
||||
"succ",
|
||||
"Успешно",
|
||||
"Этот комментарий больше не будет показыватся.<br/><a href='/al_comments/spam?$id'>Отметить как спам</a>?"
|
||||
tr("success"),
|
||||
tr("comment_will_not_appear")
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,6 +41,7 @@ final class GiftsPresenter extends OpenVKPresenter
|
|||
|
||||
$this->template->user = $user;
|
||||
$this->template->iterator = $cats;
|
||||
$this->template->count = $this->gifts->getCategoriesCount();
|
||||
$this->template->_template = "Gifts/Menu.xml";
|
||||
}
|
||||
|
||||
|
@ -49,7 +50,7 @@ final class GiftsPresenter extends OpenVKPresenter
|
|||
$user = $this->users->get((int) ($this->queryParam("user") ?? 0));
|
||||
$cat = $this->gifts->getCat((int) ($this->queryParam("pack") ?? 0));
|
||||
if(!$user || !$cat)
|
||||
$this->flashFail("err", "Не удалось подарить", "Пользователь или набор не существуют.");
|
||||
$this->flashFail("err", tr("error_when_gifting"), tr("error_user_not_exists"));
|
||||
|
||||
$this->template->page = $page = (int) ($this->queryParam("p") ?? 1);
|
||||
$gifts = $cat->getGifts($page, null, $this->template->count);
|
||||
|
@ -66,14 +67,14 @@ final class GiftsPresenter extends OpenVKPresenter
|
|||
$gift = $this->gifts->get((int) ($this->queryParam("elid") ?? 0));
|
||||
$cat = $this->gifts->getCat((int) ($this->queryParam("pack") ?? 0));
|
||||
if(!$user || !$cat || !$gift || !$cat->hasGift($gift))
|
||||
$this->flashFail("err", "Не удалось подарить", "Не удалось подтвердить права на подарок.");
|
||||
$this->flashFail("err", tr("error_when_gifting"), tr("error_no_rights_gifts"));
|
||||
|
||||
if(!$gift->canUse($this->user->identity))
|
||||
$this->flashFail("err", "Не удалось подарить", "У вас больше не осталось таких подарков.");
|
||||
$this->flashFail("err", tr("error_when_gifting"), tr("error_no_more_gifts"));
|
||||
|
||||
$coinsLeft = $this->user->identity->getCoins() - $gift->getPrice();
|
||||
if($coinsLeft < 0)
|
||||
$this->flashFail("err", "Не удалось подарить", "Ору нищ не пук.");
|
||||
$this->flashFail("err", tr("error_when_gifting"), tr("error_no_money"));
|
||||
|
||||
$this->template->_template = "Gifts/Confirm.xml";
|
||||
if($_SERVER["REQUEST_METHOD"] !== "POST") {
|
||||
|
@ -91,7 +92,7 @@ final class GiftsPresenter extends OpenVKPresenter
|
|||
$user->gift($this->user->identity, $gift, $comment, !is_null($this->postParam("anonymous")));
|
||||
$gift->used();
|
||||
|
||||
$this->flash("succ", "Подарок отправлен", "Вы отправили подарок <b>" . $user->getFirstName() . "</b> за " . $gift->getPrice() . " голосов.");
|
||||
$this->flash("succ", tr("gift_sent"), tr("gift_sent_desc", $user->getFirstName(), $gift->getPrice()));
|
||||
$this->redirect($user->getURL());
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<?php declare(strict_types=1);
|
||||
namespace openvk\Web\Presenters;
|
||||
use openvk\Web\Models\Entities\{Club, Photo};
|
||||
use openvk\Web\Models\Entities\{Club, Photo, Post};
|
||||
use Nette\InvalidStateException;
|
||||
use openvk\Web\Models\Entities\Notifications\ClubModeratorNotification;
|
||||
use openvk\Web\Models\Repositories\{Clubs, Users, Albums, Managers, Topics};
|
||||
|
@ -24,10 +24,14 @@ final class GroupPresenter extends OpenVKPresenter
|
|||
if(!$club) {
|
||||
$this->notFound();
|
||||
} else {
|
||||
$this->template->albums = (new Albums)->getClubAlbums($club, 1, 3);
|
||||
$this->template->albumsCount = (new Albums)->getClubAlbumsCount($club);
|
||||
$this->template->topics = (new Topics)->getLastTopics($club, 3);
|
||||
$this->template->topicsCount = (new Topics)->getClubTopicsCount($club);
|
||||
if ($club->isBanned()) {
|
||||
$this->template->_template = "Group/Banned.xml";
|
||||
} else {
|
||||
$this->template->albums = (new Albums)->getClubAlbums($club, 1, 3);
|
||||
$this->template->albumsCount = (new Albums)->getClubAlbumsCount($club);
|
||||
$this->template->topics = (new Topics)->getLastTopics($club, 3);
|
||||
$this->template->topicsCount = (new Topics)->getClubTopicsCount($club);
|
||||
}
|
||||
|
||||
$this->template->club = $club;
|
||||
}
|
||||
|
@ -39,7 +43,7 @@ final class GroupPresenter extends OpenVKPresenter
|
|||
$this->willExecuteWriteAction();
|
||||
|
||||
if($_SERVER["REQUEST_METHOD"] === "POST") {
|
||||
if(!empty($this->postParam("name")))
|
||||
if(!empty($this->postParam("name")) && mb_strlen(trim($this->postParam("name"))) > 0)
|
||||
{
|
||||
$club = new Club;
|
||||
$club->setName($this->postParam("name"));
|
||||
|
@ -50,7 +54,7 @@ final class GroupPresenter extends OpenVKPresenter
|
|||
$club->save();
|
||||
} catch(\PDOException $ex) {
|
||||
if($ex->getCode() == 23000)
|
||||
$this->flashFail("err", "Ошибка", "Произошла ошибка на стороне сервера. Обратитесь к системному администратору.");
|
||||
$this->flashFail("err", tr("error"), tr("error_on_server_side"));
|
||||
else
|
||||
throw $ex;
|
||||
}
|
||||
|
@ -58,7 +62,7 @@ final class GroupPresenter extends OpenVKPresenter
|
|||
$club->toggleSubscription($this->user->identity);
|
||||
$this->redirect("/club" . $club->getId());
|
||||
}else{
|
||||
$this->flashFail("err", "Ошибка", "Вы не ввели название группы.");
|
||||
$this->flashFail("err", tr("error"), tr("error_no_group_name"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -72,6 +76,7 @@ final class GroupPresenter extends OpenVKPresenter
|
|||
|
||||
$club = $this->clubs->get((int) $this->postParam("id"));
|
||||
if(!$club) exit("Invalid state");
|
||||
if ($club->isBanned()) $this->flashFail("err", tr("error"), tr("forbidden"));
|
||||
|
||||
$club->toggleSubscription($this->user->identity);
|
||||
|
||||
|
@ -83,6 +88,8 @@ final class GroupPresenter extends OpenVKPresenter
|
|||
$this->assertUserLoggedIn();
|
||||
|
||||
$this->template->club = $this->clubs->get($id);
|
||||
if ($this->template->club->isBanned()) $this->flashFail("err", tr("error"), tr("forbidden"));
|
||||
|
||||
$this->template->onlyShowManagers = $this->queryParam("onlyAdmins") == "1";
|
||||
if($this->template->onlyShowManagers) {
|
||||
$this->template->followers = NULL;
|
||||
|
@ -118,12 +125,14 @@ final class GroupPresenter extends OpenVKPresenter
|
|||
$this->badRequest();
|
||||
|
||||
$club = $this->clubs->get($id);
|
||||
if ($club->isBanned()) $this->flashFail("err", tr("error"), tr("forbidden"));
|
||||
|
||||
$user = (new Users)->get((int) $user);
|
||||
if(!$user || !$club)
|
||||
$this->notFound();
|
||||
|
||||
if(!$club->canBeModifiedBy($this->user->identity ?? NULL))
|
||||
$this->flashFail("err", "Ошибка доступа", "У вас недостаточно прав, чтобы изменять этот ресурс.");
|
||||
$this->flashFail("err", tr("error_access_denied_short"), tr("error_access_denied"));
|
||||
|
||||
if(!is_null($hidden)) {
|
||||
if($club->getOwner()->getId() == $user->getId()) {
|
||||
|
@ -141,9 +150,9 @@ final class GroupPresenter extends OpenVKPresenter
|
|||
}
|
||||
|
||||
if($hidden) {
|
||||
$this->flashFail("succ", "Операция успешна", "Теперь " . $user->getCanonicalName() . " будет показываться как обычный подписчик всем кроме других администраторов");
|
||||
$this->flashFail("succ", tr("success_action"), tr("x_is_now_hidden", $user->getCanonicalName()));
|
||||
} else {
|
||||
$this->flashFail("succ", "Операция успешна", "Теперь все будут знать про то что " . $user->getCanonicalName() . " - администратор");
|
||||
$this->flashFail("succ", tr("success_action"), tr("x_is_now_showed", $user->getCanonicalName()));
|
||||
}
|
||||
} elseif($removeComment) {
|
||||
if($club->getOwner()->getId() == $user->getId()) {
|
||||
|
@ -155,11 +164,11 @@ final class GroupPresenter extends OpenVKPresenter
|
|||
$manager->save();
|
||||
}
|
||||
|
||||
$this->flashFail("succ", "Операция успешна", "Комментарий к администратору удален");
|
||||
$this->flashFail("succ", tr("success_action"), tr("comment_is_deleted"));
|
||||
} elseif($comment) {
|
||||
if(mb_strlen($comment) > 36) {
|
||||
$commentLength = (string) mb_strlen($comment);
|
||||
$this->flashFail("err", "Ошибка", "Комментарий слишком длинный ($commentLength символов вместо 36 символов)");
|
||||
$this->flashFail("err", tr("error"), tr("comment_is_too_long", $commentLength));
|
||||
}
|
||||
|
||||
if($club->getOwner()->getId() == $user->getId()) {
|
||||
|
@ -171,16 +180,16 @@ final class GroupPresenter extends OpenVKPresenter
|
|||
$manager->save();
|
||||
}
|
||||
|
||||
$this->flashFail("succ", "Операция успешна", "Комментарий к администратору изменён");
|
||||
$this->flashFail("succ", tr("success_action"), tr("comment_is_changed"));
|
||||
}else{
|
||||
if($club->canBeModifiedBy($user)) {
|
||||
$club->removeManager($user);
|
||||
$this->flashFail("succ", "Операция успешна", $user->getCanonicalName() . " более не администратор.");
|
||||
$this->flashFail("succ", tr("success_action"), tr("x_no_more_admin", $user->getCanonicalName()));
|
||||
} else {
|
||||
$club->addManager($user);
|
||||
|
||||
(new ClubModeratorNotification($user, $club, $this->user->identity))->emit();
|
||||
$this->flashFail("succ", "Операция успешна", $user->getCanonicalName() . " назначен(а) администратором.");
|
||||
$this->flashFail("succ", tr("success_action"), tr("x_is_admin", $user->getCanonicalName()));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -194,6 +203,8 @@ final class GroupPresenter extends OpenVKPresenter
|
|||
$club = $this->clubs->get($id);
|
||||
if(!$club || !$club->canBeModifiedBy($this->user->identity))
|
||||
$this->notFound();
|
||||
else if ($club->isBanned())
|
||||
$this->flashFail("err", tr("error"), tr("forbidden"));
|
||||
else
|
||||
$this->template->club = $club;
|
||||
|
||||
|
@ -201,7 +212,7 @@ final class GroupPresenter extends OpenVKPresenter
|
|||
if(!$club->setShortcode( empty($this->postParam("shortcode")) ? NULL : $this->postParam("shortcode") ))
|
||||
$this->flashFail("err", tr("error"), tr("error_shorturl_incorrect"));
|
||||
|
||||
$club->setName(empty($this->postParam("name")) ? $club->getName() : $this->postParam("name"));
|
||||
$club->setName((empty($this->postParam("name")) || mb_strlen(trim($this->postParam("name"))) === 0) ? $club->getName() : $this->postParam("name"));
|
||||
$club->setAbout(empty($this->postParam("about")) ? NULL : $this->postParam("about"));
|
||||
$club->setWall(empty($this->postParam("wall")) ? 0 : 1);
|
||||
$club->setAdministrators_List_Display(empty($this->postParam("administrators_list_display")) ? 0 : $this->postParam("administrators_list_display"));
|
||||
|
@ -234,7 +245,7 @@ final class GroupPresenter extends OpenVKPresenter
|
|||
(new Albums)->getClubAvatarAlbum($club)->addPhoto($photo);
|
||||
} catch(ISE $ex) {
|
||||
$name = $album->getName();
|
||||
$this->flashFail("err", "Неизвестная ошибка", "Не удалось сохранить фотографию.");
|
||||
$this->flashFail("err", tr("error"), tr("error_when_uploading_photo"));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -242,15 +253,59 @@ final class GroupPresenter extends OpenVKPresenter
|
|||
$club->save();
|
||||
} catch(\PDOException $ex) {
|
||||
if($ex->getCode() == 23000)
|
||||
$this->flashFail("err", "Ошибка", "Произошла ошибка на стороне сервера. Обратитесь к системному администратору.");
|
||||
$this->flashFail("err", tr("error"), tr("error_on_server_side"));
|
||||
else
|
||||
throw $ex;
|
||||
}
|
||||
|
||||
$this->flash("succ", "Изменения сохранены", "Новые данные появятся в вашей группе.");
|
||||
$this->flash("succ", tr("changes_saved"), tr("new_changes_desc"));
|
||||
}
|
||||
}
|
||||
|
||||
function renderSetAvatar(int $id)
|
||||
{
|
||||
$photo = new Photo;
|
||||
$club = $this->clubs->get($id);
|
||||
if ($club->isBanned()) $this->flashFail("err", tr("error"), tr("forbidden"));
|
||||
if($_SERVER["REQUEST_METHOD"] === "POST" && $_FILES["ava"]["error"] === UPLOAD_ERR_OK) {
|
||||
try {
|
||||
$anon = OPENVK_ROOT_CONF["openvk"]["preferences"]["wall"]["anonymousPosting"]["enable"];
|
||||
if($anon && $this->user->id === $club->getOwner()->getId())
|
||||
$anon = $club->isOwnerHidden();
|
||||
else if($anon)
|
||||
$anon = $club->getManager($this->user->identity)->isHidden();
|
||||
$photo->setOwner($this->user->id);
|
||||
$photo->setDescription("Club image");
|
||||
$photo->setFile($_FILES["ava"]);
|
||||
$photo->setCreated(time());
|
||||
$photo->setAnonymous($anon);
|
||||
$photo->save();
|
||||
|
||||
(new Albums)->getClubAvatarAlbum($club)->addPhoto($photo);
|
||||
|
||||
$flags = 0;
|
||||
$flags |= 0b00010000;
|
||||
$flags |= 0b10000000;
|
||||
|
||||
$post = new Post;
|
||||
$post->setOwner($this->user->id);
|
||||
$post->setWall($club->getId()*-1);
|
||||
$post->setCreated(time());
|
||||
$post->setContent("");
|
||||
$post->setFlags($flags);
|
||||
$post->save();
|
||||
$post->attach($photo);
|
||||
|
||||
} catch(ISE $ex) {
|
||||
$name = $album->getName();
|
||||
$this->flashFail("err", tr("error"), tr("error_when_uploading_photo"));
|
||||
}
|
||||
}
|
||||
$this->returnJson([
|
||||
"url" => $photo->getURL(),
|
||||
"id" => $photo->getPrettyId()
|
||||
]);
|
||||
}
|
||||
function renderEditBackdrop(int $id): void
|
||||
{
|
||||
$this->assertUserLoggedIn();
|
||||
|
@ -295,11 +350,13 @@ final class GroupPresenter extends OpenVKPresenter
|
|||
$this->assertUserLoggedIn();
|
||||
|
||||
if(!eventdb())
|
||||
$this->flashFail("err", "Ошибка подключения", "Не удалось подключится к службе телеметрии.");
|
||||
$this->flashFail("err", tr("connection_error"), tr("connection_error_desc"));
|
||||
|
||||
$club = $this->clubs->get($id);
|
||||
if(!$club->canBeModifiedBy($this->user->identity))
|
||||
$this->notFound();
|
||||
else if ($club->isBanned())
|
||||
$this->flashFail("err", tr("error"), tr("forbidden"));
|
||||
else
|
||||
$this->template->club = $club;
|
||||
|
||||
|
@ -332,6 +389,7 @@ final class GroupPresenter extends OpenVKPresenter
|
|||
$this->flashFail("err", tr("error"), tr("incorrect_password"));
|
||||
|
||||
$club = $this->clubs->get($id);
|
||||
if ($club->isBanned()) $this->flashFail("err", tr("error"), tr("forbidden"));
|
||||
$newOwner = (new Users)->get($newOwnerId);
|
||||
if($this->user->id !== $club->getOwner()->getId())
|
||||
$this->flashFail("err", tr("error"), tr("forbidden"));
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
<?php declare(strict_types=1);
|
||||
namespace openvk\Web\Presenters;
|
||||
use openvk\Web\Models\Repositories\{Posts, Comments};
|
||||
use MessagePack\MessagePack;
|
||||
use Chandler\Session\Session;
|
||||
|
||||
|
@ -95,4 +96,41 @@ final class InternalAPIPresenter extends OpenVKPresenter
|
|||
]);
|
||||
}
|
||||
}
|
||||
|
||||
function renderGetPhotosFromPost(int $owner_id, int $post_id) {
|
||||
if($_SERVER["REQUEST_METHOD"] !== "POST") {
|
||||
header("HTTP/1.1 405 Method Not Allowed");
|
||||
exit("иди нахуй заебал");
|
||||
}
|
||||
|
||||
if($this->postParam("parentType", false) == "post") {
|
||||
$post = (new Posts)->getPostById($owner_id, $post_id);
|
||||
} else {
|
||||
$post = (new Comments)->get($post_id);
|
||||
}
|
||||
|
||||
|
||||
if(is_null($post)) {
|
||||
$this->returnJson([
|
||||
"success" => 0
|
||||
]);
|
||||
} else {
|
||||
$response = [];
|
||||
$attachments = $post->getChildren();
|
||||
foreach($attachments as $attachment)
|
||||
{
|
||||
if($attachment instanceof \openvk\Web\Models\Entities\Photo)
|
||||
{
|
||||
$response[] = [
|
||||
"url" => $attachment->getURLBySizeId('normal'),
|
||||
"id" => $attachment->getPrettyId()
|
||||
];
|
||||
}
|
||||
}
|
||||
$this->returnJson([
|
||||
"success" => 1,
|
||||
"body" => $response
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -57,6 +57,11 @@ final class MessengerPresenter extends OpenVKPresenter
|
|||
$correspondent = $this->getCorrespondent($sel);
|
||||
if(!$correspondent)
|
||||
$this->notFound();
|
||||
|
||||
if(!$this->user->identity->getPrivacyPermission('messages.write', $correspondent))
|
||||
{
|
||||
$this->flash("err", tr("warning"), tr("user_may_not_reply"));
|
||||
}
|
||||
|
||||
$this->template->selId = $sel;
|
||||
$this->template->correspondent = $correspondent;
|
||||
|
|
384
Web/Presenters/NoSpamPresenter.php
Normal file
384
Web/Presenters/NoSpamPresenter.php
Normal file
|
@ -0,0 +1,384 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
namespace openvk\Web\Presenters;
|
||||
|
||||
use Nette\Database\DriverException;
|
||||
use Nette\Utils\Finder;
|
||||
use Chandler\Database\DatabaseConnection;
|
||||
use openvk\Web\Models\Entities\Club;
|
||||
use openvk\Web\Models\Entities\Comment;
|
||||
use Chandler\Database\Log;
|
||||
use openvk\Web\Models\Entities\NoSpamLog;
|
||||
use openvk\Web\Models\Entities\User;
|
||||
use openvk\Web\Models\Repositories\ChandlerUsers;
|
||||
use Chandler\Database\Logs;
|
||||
use openvk\Web\Models\Repositories\NoSpamLogs;
|
||||
use openvk\Web\Models\Repositories\Users;
|
||||
|
||||
final class NoSpamPresenter extends OpenVKPresenter
|
||||
{
|
||||
protected $banTolerant = true;
|
||||
protected $deactivationTolerant = true;
|
||||
protected $presenterName = "nospam";
|
||||
|
||||
const ENTITIES_NAMESPACE = "openvk\\Web\\Models\\Entities";
|
||||
|
||||
function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
function renderIndex(): void
|
||||
{
|
||||
$this->assertUserLoggedIn();
|
||||
$this->assertPermission('openvk\Web\Models\Entities\TicketReply', 'write', 0);
|
||||
|
||||
$targetDir = __DIR__ . '/../Models/Entities/';
|
||||
$mode = in_array($this->queryParam("act"), ["form", "templates", "rollback", "reports"]) ? $this->queryParam("act") : "form";
|
||||
|
||||
if ($mode === "form") {
|
||||
$this->template->_template = "NoSpam/Index";
|
||||
$foundClasses = [];
|
||||
foreach (Finder::findFiles('*.php')->from($targetDir) as $file) {
|
||||
$content = file_get_contents($file->getPathname());
|
||||
$namespacePattern = '/namespace\s+([^\s;]+)/';
|
||||
$classPattern = '/class\s+([^\s{]+)/';
|
||||
preg_match($namespacePattern, $content, $namespaceMatches);
|
||||
preg_match($classPattern, $content, $classMatches);
|
||||
|
||||
if (isset($namespaceMatches[1]) && isset($classMatches[1])) {
|
||||
$classNamespace = trim($namespaceMatches[1]);
|
||||
$className = trim($classMatches[1]);
|
||||
$fullClassName = $classNamespace . '\\' . $className;
|
||||
|
||||
if ($classNamespace === NoSpamPresenter::ENTITIES_NAMESPACE && class_exists($fullClassName)) {
|
||||
$foundClasses[] = $className;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$models = [];
|
||||
|
||||
foreach ($foundClasses as $class) {
|
||||
$r = new \ReflectionClass(NoSpamPresenter::ENTITIES_NAMESPACE . "\\$class");
|
||||
if (!$r->isAbstract() && $r->getName() !== NoSpamPresenter::ENTITIES_NAMESPACE . "\\Correspondence")
|
||||
$models[] = $class;
|
||||
}
|
||||
$this->template->models = $models;
|
||||
} else if ($mode === "templates") {
|
||||
$this->template->_template = "NoSpam/Templates.xml";
|
||||
$filter = [];
|
||||
if ($this->queryParam("id")) {
|
||||
$filter["id"] = (int)$this->queryParam("id");
|
||||
}
|
||||
$this->template->templates = iterator_to_array((new NoSpamLogs)->getList($filter));
|
||||
} else if ($mode === "reports") {
|
||||
$this->redirect("/scumfeed");
|
||||
} else {
|
||||
$template = (new NoSpamLogs)->get((int)$this->postParam("id"));
|
||||
if (!$template || $template->isRollbacked())
|
||||
$this->returnJson(["success" => false, "error" => "Шаблон не найден"]);
|
||||
|
||||
$model = NoSpamPresenter::ENTITIES_NAMESPACE . "\\" . $template->getModel();
|
||||
$items = $template->getItems();
|
||||
if (count($items) > 0) {
|
||||
$db = DatabaseConnection::i()->getContext();
|
||||
|
||||
$unbanned_ids = [];
|
||||
foreach ($items as $_item) {
|
||||
try {
|
||||
$item = new $model;
|
||||
$table_name = $item->getTableName();
|
||||
$item = $db->table($table_name)->get((int)$_item);
|
||||
if (!$item) continue;
|
||||
|
||||
$item = new $model($item);
|
||||
|
||||
if (key_exists("deleted", $item->unwrap()) && $item->isDeleted()) {
|
||||
$item->setDeleted(0);
|
||||
$item->save();
|
||||
}
|
||||
|
||||
if (in_array($template->getTypeRaw(), [2, 3])) {
|
||||
$owner = NULL;
|
||||
$methods = ["getOwner", "getUser", "getRecipient", "getInitiator"];
|
||||
|
||||
if (method_exists($item, "ban")) {
|
||||
$owner = $item;
|
||||
} else {
|
||||
foreach ($methods as $method) {
|
||||
if (method_exists($item, $method)) {
|
||||
$owner = $item->$method();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$_id = ($owner instanceof Club ? $owner->getId() * -1 : $owner->getId());
|
||||
|
||||
if (!in_array($_id, $unbanned_ids)) {
|
||||
$owner->unban($this->user->id);
|
||||
$unbanned_ids[] = $_id;
|
||||
}
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
$this->returnJson(["success" => false, "error" => $e->getMessage()]);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$this->returnJson(["success" => false, "error" => "Объекты не найдены"]);
|
||||
}
|
||||
|
||||
$template->setRollback(true);
|
||||
$template->save();
|
||||
|
||||
$this->returnJson(["success" => true]);
|
||||
}
|
||||
}
|
||||
|
||||
function renderSearch(): void
|
||||
{
|
||||
$this->assertUserLoggedIn();
|
||||
$this->assertPermission('openvk\Web\Models\Entities\TicketReply', 'write', 0);
|
||||
$this->assertNoCSRF();
|
||||
$this->willExecuteWriteAction();
|
||||
|
||||
function searchByAdditionalParams(?string $table = NULL, ?string $where = NULL, ?string $ip = NULL, ?string $useragent = NULL, ?int $ts = NULL, ?int $te = NULL, $user = NULL)
|
||||
{
|
||||
$db = DatabaseConnection::i()->getContext();
|
||||
if ($table && ($ip || $useragent || $ts || $te || $user)) {
|
||||
$conditions = [];
|
||||
|
||||
if ($ip) $conditions[] = "`ip` REGEXP '$ip'";
|
||||
if ($useragent) $conditions[] = "`useragent` REGEXP '$useragent'";
|
||||
if ($ts) $conditions[] = "`ts` < $ts";
|
||||
if ($te) $conditions[] = "`ts` > $te";
|
||||
if ($user) {
|
||||
$users = new Users;
|
||||
|
||||
$_user = $users->getByChandlerUser((new ChandlerUsers)->getById($user))
|
||||
?? $users->get((int)$user)
|
||||
?? $users->getByAddress($user)
|
||||
?? NULL;
|
||||
|
||||
if ($_user) {
|
||||
$conditions[] = "`user` = '" . $_user->getChandlerGUID() . "'";
|
||||
}
|
||||
}
|
||||
|
||||
$whereStart = "WHERE `object_table` = '$table'";
|
||||
if ($table === "profiles") {
|
||||
$whereStart .= "AND `type` = 0";
|
||||
}
|
||||
|
||||
$conditions = count($conditions) > 0 ? "AND (" . implode(" AND ", $conditions) . ")" : "";
|
||||
$response = [];
|
||||
|
||||
if ($conditions) {
|
||||
$logs = $db->query("SELECT * FROM `ChandlerLogs` $whereStart $conditions GROUP BY `object_id`, `object_model`");
|
||||
|
||||
foreach ($logs as $log) {
|
||||
$log = (new Logs)->get($log->id);
|
||||
$object = $log->getObject()->unwrap();
|
||||
|
||||
if (!$object) continue;
|
||||
if ($where) {
|
||||
if (str_starts_with($where, " AND")) {
|
||||
$where = substr_replace($where, "", 0, strlen(" AND"));
|
||||
}
|
||||
|
||||
$a = $db->query("SELECT * FROM `$table` WHERE $where")->fetchAll();
|
||||
foreach ($a as $o) {
|
||||
if ($object->id == $o["id"]) {
|
||||
$response[] = $object;
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
$response[] = $object;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
$response = [];
|
||||
$processed = 0;
|
||||
|
||||
$where = $this->postParam("where");
|
||||
$ip = addslashes($this->postParam("ip"));
|
||||
$useragent = addslashes($this->postParam("useragent"));
|
||||
$searchTerm = addslashes($this->postParam("q"));
|
||||
$ts = (int)$this->postParam("ts");
|
||||
$te = (int)$this->postParam("te");
|
||||
$user = addslashes($this->postParam("user"));
|
||||
|
||||
if ($where) {
|
||||
$where = explode(";", $where)[0];
|
||||
}
|
||||
|
||||
if (!$ip && !$useragent && !$searchTerm && !$ts && !$te && !$where && !$searchTerm && !$user)
|
||||
$this->returnJson(["success" => false, "error" => "Нет запроса. Заполните поле \"подстрока\" или введите запрос \"WHERE\" в поле под ним."]);
|
||||
|
||||
$models = explode(",", $this->postParam("models"));
|
||||
|
||||
foreach ($models as $_model) {
|
||||
$model_name = NoSpamPresenter::ENTITIES_NAMESPACE . "\\" . $_model;
|
||||
if (!class_exists($model_name)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$model = new $model_name;
|
||||
|
||||
$c = new \ReflectionClass($model_name);
|
||||
if ($c->isAbstract() || $c->getName() == NoSpamPresenter::ENTITIES_NAMESPACE . "\\Correspondence") {
|
||||
continue;
|
||||
}
|
||||
|
||||
$db = DatabaseConnection::i()->getContext();
|
||||
$table = $model->getTableName();
|
||||
$columns = $db->getStructure()->getColumns($table);
|
||||
|
||||
if ($searchTerm) {
|
||||
$conditions = [];
|
||||
$need_deleted = false;
|
||||
foreach ($columns as $column) {
|
||||
if ($column["name"] == "deleted") {
|
||||
$need_deleted = true;
|
||||
} else {
|
||||
$conditions[] = "`$column[name]` REGEXP '$searchTerm'";
|
||||
}
|
||||
}
|
||||
$conditions = implode(" OR ", $conditions);
|
||||
|
||||
$where = ($this->postParam("where") ? " AND ($conditions)" : "($conditions)");
|
||||
if ($need_deleted) $where .= " AND (`deleted` = 0)";
|
||||
}
|
||||
|
||||
$rows = [];
|
||||
|
||||
if (str_starts_with($where, " AND")) {
|
||||
if ($searchTerm && !$this->postParam("where")) {
|
||||
$where = substr_replace($where, "", 0, strlen(" AND"));
|
||||
} else {
|
||||
$where = "(" . $this->postParam("where") . ")" . $where;
|
||||
}
|
||||
}
|
||||
|
||||
if ($ip || $useragent || $ts || $te || $user) {
|
||||
$rows = searchByAdditionalParams($table, $where, $ip, $useragent, $ts, $te, $user);
|
||||
} else {
|
||||
if (!$where) {
|
||||
$rows = [];
|
||||
} else {
|
||||
$result = $db->query("SELECT * FROM `$table` WHERE $where");
|
||||
$rows = $result->fetchAll();
|
||||
}
|
||||
}
|
||||
|
||||
if (!in_array((int)$this->postParam("ban"), [1, 2, 3])) {
|
||||
foreach ($rows as $key => $object) {
|
||||
$object = (array)$object;
|
||||
$_obj = [];
|
||||
foreach ($object as $key => $value) {
|
||||
foreach ($columns as $column) {
|
||||
if ($column["name"] === $key && in_array(strtoupper($column["nativetype"]), ["BLOB", "BINARY", "VARBINARY", "TINYBLOB", "MEDIUMBLOB", "LONGBLOB"])) {
|
||||
$value = "[BINARY]";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$_obj[$key] = $value;
|
||||
$_obj["__model_name"] = $_model;
|
||||
}
|
||||
$response[] = $_obj;
|
||||
}
|
||||
} else {
|
||||
$ids = [];
|
||||
|
||||
foreach ($rows as $object) {
|
||||
$object = new $model_name($db->table($table)->get($object->id));
|
||||
if (!$object) continue;
|
||||
$ids[] = $object->getId();
|
||||
}
|
||||
|
||||
$log = new NoSpamLog;
|
||||
$log->setUser($this->user->id);
|
||||
$log->setModel($_model);
|
||||
if ($searchTerm) {
|
||||
$log->setRegex($searchTerm);
|
||||
} else {
|
||||
$log->setRequest($where);
|
||||
}
|
||||
$log->setBan_Type((int)$this->postParam("ban"));
|
||||
$log->setCount(count($rows));
|
||||
$log->setTime(time());
|
||||
$log->setItems(implode(",", $ids));
|
||||
$log->save();
|
||||
|
||||
$banned_ids = [];
|
||||
foreach ($rows as $object) {
|
||||
$object = new $model_name($db->table($table)->get($object->id));
|
||||
if (!$object) continue;
|
||||
|
||||
$owner = NULL;
|
||||
$methods = ["getOwner", "getUser", "getRecipient", "getInitiator"];
|
||||
|
||||
if (method_exists($object, "ban")) {
|
||||
$owner = $object;
|
||||
} else {
|
||||
foreach ($methods as $method) {
|
||||
if (method_exists($object, $method)) {
|
||||
$owner = $object->$method();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($owner instanceof User && $owner->getId() === $this->user->id) {
|
||||
if (count($rows) === 1) {
|
||||
$this->returnJson(["success" => false, "error" => "\"Производственная травма\" — Вы не можете блокировать или удалять свой же контент"]);
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (in_array((int)$this->postParam("ban"), [2, 3])) {
|
||||
$reason = mb_strlen(trim($this->postParam("ban_reason"))) > 0 ? addslashes($this->postParam("ban_reason")) : ("**content-noSpamTemplate-" . $log->getId() . "**");
|
||||
$is_forever = (string)$this->postParam("is_forever") === "true";
|
||||
$unban_time = $is_forever ? 0 : (int)$this->postParam("unban_time") ?? NULL;
|
||||
|
||||
if ($owner) {
|
||||
$_id = ($owner instanceof Club ? $owner->getId() * -1 : $owner->getId());
|
||||
if (!in_array($_id, $banned_ids)) {
|
||||
if ($owner instanceof User) {
|
||||
if (!$unban_time && !$is_forever)
|
||||
$unban_time = time() + $owner->getNewBanTime();
|
||||
|
||||
$owner->ban($reason, false, $unban_time, $this->user->id);
|
||||
} else {
|
||||
$owner->ban("Подозрительная активность");
|
||||
}
|
||||
|
||||
$banned_ids[] = $_id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (in_array((int)$this->postParam("ban"), [1, 3]))
|
||||
$object->delete();
|
||||
}
|
||||
|
||||
$processed++;
|
||||
}
|
||||
}
|
||||
|
||||
$this->returnJson(["success" => true, "processed" => $processed, "count" => count($response), "list" => $response]);
|
||||
} catch (\Throwable $e) {
|
||||
$this->returnJson(["success" => false, "error" => $e->getMessage()]);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -107,7 +107,7 @@ final class NotesPresenter extends OpenVKPresenter
|
|||
if(!$note || $note->getOwner()->getId() !== $owner || $note->isDeleted())
|
||||
$this->notFound();
|
||||
if(is_null($this->user) || !$note->canBeModifiedBy($this->user->identity))
|
||||
$this->flashFail("err", "Ошибка доступа", "Недостаточно прав для модификации данного ресурса.");
|
||||
$this->flashFail("err", tr("error_access_denied_short"), tr("error_access_denied"));
|
||||
$this->template->note = $note;
|
||||
|
||||
if($_SERVER["REQUEST_METHOD"] === "POST") {
|
||||
|
@ -135,11 +135,11 @@ final class NotesPresenter extends OpenVKPresenter
|
|||
if(!$note) $this->notFound();
|
||||
if($note->getOwner()->getId() . "_" . $note->getId() !== $owner . "_" . $id || $note->isDeleted()) $this->notFound();
|
||||
if(is_null($this->user) || !$note->canBeModifiedBy($this->user->identity))
|
||||
$this->flashFail("err", "Ошибка доступа", "Недостаточно прав для модификации данного ресурса.");
|
||||
$this->flashFail("err", tr("error_access_denied_short"), tr("error_access_denied"));
|
||||
|
||||
$name = $note->getName();
|
||||
$note->delete();
|
||||
$this->flash("succ", "Заметка удалена", "Заметка \"$name\" была успешно удалена.");
|
||||
$this->flash("succ", tr("note_is_deleted"), tr("note_x_is_now_deleted", $name));
|
||||
$this->redirect("/notes" . $this->user->id);
|
||||
}
|
||||
}
|
||||
|
|
10
Web/Presenters/OpenVKPresenter.php
Executable file → Normal file
10
Web/Presenters/OpenVKPresenter.php
Executable file → Normal file
|
@ -7,7 +7,7 @@ use Chandler\Security\Authenticator;
|
|||
use Latte\Engine as TemplatingEngine;
|
||||
use openvk\Web\Models\Entities\IP;
|
||||
use openvk\Web\Themes\Themepacks;
|
||||
use openvk\Web\Models\Repositories\{IPs, Users, APITokens, Tickets};
|
||||
use openvk\Web\Models\Repositories\{IPs, Users, APITokens, Tickets, Reports, CurrentUser};
|
||||
use WhichBrowser;
|
||||
|
||||
abstract class OpenVKPresenter extends SimplePresenter
|
||||
|
@ -214,6 +214,7 @@ abstract class OpenVKPresenter extends SimplePresenter
|
|||
$this->user->id = $this->user->identity->getId();
|
||||
$this->template->thisUser = $this->user->identity;
|
||||
$this->template->userTainted = $user->isTainted();
|
||||
CurrentUser::get($this->user->identity, $_SERVER["REMOTE_ADDR"], $_SERVER["HTTP_USER_AGENT"]);
|
||||
|
||||
if($this->user->identity->isDeleted() && !$this->deactivationTolerant) {
|
||||
if($this->user->identity->isDeactivated()) {
|
||||
|
@ -257,12 +258,15 @@ abstract class OpenVKPresenter extends SimplePresenter
|
|||
$cacheTime = 0; # Force no cache
|
||||
if($this->user->identity->onlineStatus() == 0 && !($this->user->identity->isDeleted() || $this->user->identity->isBanned())) {
|
||||
$this->user->identity->setOnline(time());
|
||||
$this->user->identity->save();
|
||||
$this->user->identity->setClient_name(NULL);
|
||||
$this->user->identity->save(false);
|
||||
}
|
||||
|
||||
$this->template->ticketAnsweredCount = (new Tickets)->getTicketsCountByUserId($this->user->id, 1);
|
||||
if($user->can("write")->model("openvk\Web\Models\Entities\TicketReply")->whichBelongsTo(0))
|
||||
if($user->can("write")->model("openvk\Web\Models\Entities\TicketReply")->whichBelongsTo(0)) {
|
||||
$this->template->helpdeskTicketNotAnsweredCount = (new Tickets)->getTicketCount(0);
|
||||
$this->template->reportNotAnsweredCount = (new Reports)->getReportsCount(0);
|
||||
}
|
||||
}
|
||||
|
||||
header("X-OpenVK-User-Validated: $userValidated");
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<?php declare(strict_types=1);
|
||||
namespace openvk\Web\Presenters;
|
||||
use openvk\Web\Models\Entities\{Club, Photo, Album};
|
||||
use openvk\Web\Models\Entities\{Club, Photo, Album, User};
|
||||
use openvk\Web\Models\Repositories\{Photos, Albums, Users, Clubs};
|
||||
use Nette\InvalidStateException as ISE;
|
||||
|
||||
|
@ -27,7 +27,7 @@ final class PhotosPresenter extends OpenVKPresenter
|
|||
if(!$user) $this->notFound();
|
||||
if (!$user->getPrivacyPermission('photos.read', $this->user->identity ?? NULL))
|
||||
$this->flashFail("err", tr("forbidden"), tr("forbidden_comment"));
|
||||
$this->template->albums = $this->albums->getUserAlbums($user, $this->queryParam("p") ?? 1);
|
||||
$this->template->albums = $this->albums->getUserAlbums($user, (int)($this->queryParam("p") ?? 1));
|
||||
$this->template->count = $this->albums->getUserAlbumsCount($user);
|
||||
$this->template->owner = $user;
|
||||
$this->template->canEdit = false;
|
||||
|
@ -36,7 +36,7 @@ final class PhotosPresenter extends OpenVKPresenter
|
|||
} else {
|
||||
$club = (new Clubs)->get(abs($owner));
|
||||
if(!$club) $this->notFound();
|
||||
$this->template->albums = $this->albums->getClubAlbums($club, $this->queryParam("p") ?? 1);
|
||||
$this->template->albums = $this->albums->getClubAlbums($club, (int)($this->queryParam("p") ?? 1));
|
||||
$this->template->count = $this->albums->getClubAlbumsCount($club);
|
||||
$this->template->owner = $club;
|
||||
$this->template->canEdit = false;
|
||||
|
@ -46,7 +46,7 @@ final class PhotosPresenter extends OpenVKPresenter
|
|||
|
||||
$this->template->paginatorConf = (object) [
|
||||
"count" => $this->template->count,
|
||||
"page" => $this->queryParam("p") ?? 1,
|
||||
"page" => (int)($this->queryParam("p") ?? 1),
|
||||
"amount" => NULL,
|
||||
"perPage" => OPENVK_DEFAULT_PER_PAGE,
|
||||
];
|
||||
|
@ -66,7 +66,7 @@ final class PhotosPresenter extends OpenVKPresenter
|
|||
}
|
||||
|
||||
if($_SERVER["REQUEST_METHOD"] === "POST") {
|
||||
if(empty($this->postParam("name")))
|
||||
if(empty($this->postParam("name")) || mb_strlen(trim($this->postParam("name"))) === 0)
|
||||
$this->flashFail("err", tr("error"), tr("error_segmentation"));
|
||||
else if(strlen($this->postParam("name")) > 36)
|
||||
$this->flashFail("err", tr("error"), tr("error_data_too_big", "name", 36, "bytes"));
|
||||
|
@ -94,19 +94,19 @@ final class PhotosPresenter extends OpenVKPresenter
|
|||
if(!$album) $this->notFound();
|
||||
if($album->getPrettyId() !== $owner . "_" . $id || $album->isDeleted()) $this->notFound();
|
||||
if(is_null($this->user) || !$album->canBeModifiedBy($this->user->identity) || $album->isDeleted())
|
||||
$this->flashFail("err", "Ошибка доступа", "Недостаточно прав для модификации данного ресурса.");
|
||||
$this->flashFail("err", tr("error_access_denied_short"), tr("error_access_denied"));
|
||||
$this->template->album = $album;
|
||||
|
||||
if($_SERVER["REQUEST_METHOD"] === "POST") {
|
||||
if(strlen($this->postParam("name")) > 36)
|
||||
$this->flashFail("err", tr("error"), tr("error_data_too_big", "name", 36, "bytes"));
|
||||
|
||||
$album->setName(empty($this->postParam("name")) ? $album->getName() : $this->postParam("name"));
|
||||
$album->setName((empty($this->postParam("name")) || mb_strlen(trim($this->postParam("name"))) === 0) ? $album->getName() : $this->postParam("name"));
|
||||
$album->setDescription(empty($this->postParam("desc")) ? NULL : $this->postParam("desc"));
|
||||
$album->setEdited(time());
|
||||
$album->save();
|
||||
|
||||
$this->flash("succ", "Изменения сохранены", "Новые данные приняты.");
|
||||
$this->flash("succ", tr("changes_saved"), tr("new_data_accepted"));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -120,13 +120,13 @@ final class PhotosPresenter extends OpenVKPresenter
|
|||
if(!$album) $this->notFound();
|
||||
if($album->getPrettyId() !== $owner . "_" . $id || $album->isDeleted()) $this->notFound();
|
||||
if(is_null($this->user) || !$album->canBeModifiedBy($this->user->identity))
|
||||
$this->flashFail("err", "Ошибка доступа", "Недостаточно прав для модификации данного ресурса.");
|
||||
$this->flashFail("err", tr("error_access_denied_short"), tr("error_access_denied"));
|
||||
|
||||
$name = $album->getName();
|
||||
$owner = $album->getOwner();
|
||||
$album->delete();
|
||||
|
||||
$this->flash("succ", "Альбом удалён", "Альбом $name был успешно удалён.");
|
||||
$this->flash("succ", tr("album_is_deleted"), tr("album_x_is_deleted", $name));
|
||||
$this->redirect("/albums" . ($owner instanceof Club ? "-" : "") . $owner->getId());
|
||||
}
|
||||
|
||||
|
@ -147,7 +147,7 @@ final class PhotosPresenter extends OpenVKPresenter
|
|||
$this->template->photos = iterator_to_array( $album->getPhotos( (int) ($this->queryParam("p") ?? 1), 20) );
|
||||
$this->template->paginatorConf = (object) [
|
||||
"count" => $album->getPhotosCount(),
|
||||
"page" => $this->queryParam("p") ?? 1,
|
||||
"page" => (int)($this->queryParam("p") ?? 1),
|
||||
"amount" => sizeof($this->template->photos),
|
||||
"perPage" => 20,
|
||||
"atBottom" => true
|
||||
|
@ -185,6 +185,18 @@ final class PhotosPresenter extends OpenVKPresenter
|
|||
$this->renderPhoto($photo->getOwner(true)->getId(), $photo->getVirtualId());
|
||||
}
|
||||
|
||||
function renderThumbnail($id, $size): void
|
||||
{
|
||||
$photo = $this->photos->get($id);
|
||||
if(!$photo || $photo->isDeleted())
|
||||
$this->notFound();
|
||||
|
||||
if(!$photo->forceSize($size))
|
||||
chandler_http_panic(588, "Gone", "This thumbnail cannot be generated due to server misconfiguration");
|
||||
|
||||
$this->redirect($photo->getURLBySizeId($size), 8);
|
||||
}
|
||||
|
||||
function renderEditPhoto(int $ownerId, int $photoId): void
|
||||
{
|
||||
$this->assertUserLoggedIn();
|
||||
|
@ -193,13 +205,13 @@ final class PhotosPresenter extends OpenVKPresenter
|
|||
$photo = $this->photos->getByOwnerAndVID($ownerId, $photoId);
|
||||
if(!$photo) $this->notFound();
|
||||
if(is_null($this->user) || $this->user->id != $ownerId)
|
||||
$this->flashFail("err", "Ошибка доступа", "Недостаточно прав для модификации данного ресурса.");
|
||||
$this->flashFail("err", tr("error_access_denied_short"), tr("error_access_denied"));
|
||||
|
||||
if($_SERVER["REQUEST_METHOD"] === "POST") {
|
||||
$photo->setDescription(empty($this->postParam("desc")) ? NULL : $this->postParam("desc"));
|
||||
$photo->save();
|
||||
|
||||
$this->flash("succ", "Изменения сохранены", "Обновлённое описание появится на странице с фоткой.");
|
||||
$this->flash("succ", tr("changes_saved"), tr("new_description_will_appear"));
|
||||
$this->redirect("/photo" . $photo->getPrettyId());
|
||||
}
|
||||
|
||||
|
@ -209,39 +221,82 @@ final class PhotosPresenter extends OpenVKPresenter
|
|||
function renderUploadPhoto(): void
|
||||
{
|
||||
$this->assertUserLoggedIn();
|
||||
$this->willExecuteWriteAction();
|
||||
|
||||
if(is_null($this->queryParam("album")))
|
||||
$this->flashFail("err", "Неизвестная ошибка", "Не удалось сохранить фотографию в <b>DELETED</b>.");
|
||||
|
||||
[$owner, $id] = explode("_", $this->queryParam("album"));
|
||||
$album = $this->albums->get((int) $id);
|
||||
$this->willExecuteWriteAction(true);
|
||||
|
||||
if(is_null($this->queryParam("album"))) {
|
||||
$album = $this->albums->getUserWallAlbum($this->user->identity);
|
||||
} else {
|
||||
[$owner, $id] = explode("_", $this->queryParam("album"));
|
||||
$album = $this->albums->get((int) $id);
|
||||
}
|
||||
|
||||
if(!$album)
|
||||
$this->flashFail("err", "Неизвестная ошибка", "Не удалось сохранить фотографию в <b>DELETED</b>.");
|
||||
if(is_null($this->user) || !$album->canBeModifiedBy($this->user->identity))
|
||||
$this->flashFail("err", "Ошибка доступа", "Недостаточно прав для модификации данного ресурса.");
|
||||
$this->flashFail("err", tr("error"), tr("error_adding_to_deleted"), 500, true);
|
||||
|
||||
# Для быстрой загрузки фоток из пикера фотографий нужен альбом, но юзер не может загружать фото
|
||||
# в системные альбомы, так что так.
|
||||
if(is_null($this->user) || !is_null($this->queryParam("album")) && !$album->canBeModifiedBy($this->user->identity))
|
||||
$this->flashFail("err", tr("error_access_denied_short"), tr("error_access_denied"), 500, true);
|
||||
|
||||
if($_SERVER["REQUEST_METHOD"] === "POST") {
|
||||
if(!isset($_FILES["blob"]))
|
||||
$this->flashFail("err", "Нету фотографии", "Выберите файл.");
|
||||
|
||||
try {
|
||||
$photo = new Photo;
|
||||
$photo->setOwner($this->user->id);
|
||||
$photo->setDescription($this->postParam("desc"));
|
||||
$photo->setFile($_FILES["blob"]);
|
||||
$photo->setCreated(time());
|
||||
$photo->save();
|
||||
} catch(ISE $ex) {
|
||||
$name = $album->getName();
|
||||
$this->flashFail("err", "Неизвестная ошибка", "Не удалось сохранить фотографию в <b>$name</b>.");
|
||||
}
|
||||
|
||||
$album->addPhoto($photo);
|
||||
$album->setEdited(time());
|
||||
$album->save();
|
||||
if($this->queryParam("act") == "finish") {
|
||||
$result = json_decode($this->postParam("photos"), true);
|
||||
|
||||
foreach($result as $photoId => $description) {
|
||||
$phot = $this->photos->get($photoId);
|
||||
|
||||
$this->redirect("/photo" . $photo->getPrettyId() . "?from=album" . $album->getId());
|
||||
if(!$phot || $phot->isDeleted() || $phot->getOwner()->getId() != $this->user->id)
|
||||
continue;
|
||||
|
||||
if(iconv_strlen($description) > 255)
|
||||
$this->flashFail("err", tr("error"), tr("description_too_long"), 500, true);
|
||||
|
||||
$phot->setDescription($description);
|
||||
$phot->save();
|
||||
|
||||
$album = $phot->getAlbum();
|
||||
}
|
||||
|
||||
$this->returnJson(["success" => true,
|
||||
"album" => $album->getId(),
|
||||
"owner" => $album->getOwner() instanceof User ? $album->getOwner()->getId() : $album->getOwner()->getId() * -1]);
|
||||
}
|
||||
|
||||
if(!isset($_FILES))
|
||||
$this->flashFail("err", tr("no_photo"), tr("select_file"), 500, true);
|
||||
|
||||
$photos = [];
|
||||
if((int)$this->postParam("count") > 10)
|
||||
$this->flashFail("err", tr("no_photo"), "ты еблан", 500, true);
|
||||
|
||||
for($i = 0; $i < $this->postParam("count"); $i++) {
|
||||
try {
|
||||
$photo = new Photo;
|
||||
$photo->setOwner($this->user->id);
|
||||
$photo->setDescription("");
|
||||
$photo->setFile($_FILES["photo_".$i]);
|
||||
$photo->setCreated(time());
|
||||
$photo->save();
|
||||
|
||||
$photos[] = [
|
||||
"url" => $photo->getURLBySizeId("tiny"),
|
||||
"id" => $photo->getId(),
|
||||
"vid" => $photo->getVirtualId(),
|
||||
"owner" => $photo->getOwner()->getId(),
|
||||
"link" => $photo->getURL()
|
||||
];
|
||||
} catch(ISE $ex) {
|
||||
$name = $album->getName();
|
||||
$this->flashFail("err", "Неизвестная ошибка", "Не удалось сохранить фотографию в $name.", 500, true);
|
||||
}
|
||||
|
||||
$album->addPhoto($photo);
|
||||
$album->setEdited(time());
|
||||
$album->save();
|
||||
}
|
||||
|
||||
$this->returnJson(["success" => true,
|
||||
"photos" => $photos]);
|
||||
} else {
|
||||
$this->template->album = $album;
|
||||
}
|
||||
|
@ -257,7 +312,7 @@ final class PhotosPresenter extends OpenVKPresenter
|
|||
if(!$album || !$photo) $this->notFound();
|
||||
if(!$album->hasPhoto($photo)) $this->notFound();
|
||||
if(is_null($this->user) || !$album->canBeModifiedBy($this->user->identity))
|
||||
$this->flashFail("err", "Ошибка доступа", "Недостаточно прав для модификации данного ресурса.");
|
||||
$this->flashFail("err", tr("error_access_denied_short"), tr("error_access_denied"));
|
||||
|
||||
if($_SERVER["REQUEST_METHOD"] === "POST") {
|
||||
$this->assertNoCSRF();
|
||||
|
@ -265,7 +320,7 @@ final class PhotosPresenter extends OpenVKPresenter
|
|||
$album->setEdited(time());
|
||||
$album->save();
|
||||
|
||||
$this->flash("succ", "Фотография удалена", "Эта фотография была успешно удалена.");
|
||||
$this->flash("succ", tr("photo_is_deleted"), tr("photo_is_deleted_desc"));
|
||||
$this->redirect("/album" . $album->getPrettyId());
|
||||
}
|
||||
}
|
||||
|
@ -273,18 +328,26 @@ final class PhotosPresenter extends OpenVKPresenter
|
|||
function renderDeletePhoto(int $ownerId, int $photoId): void
|
||||
{
|
||||
$this->assertUserLoggedIn();
|
||||
$this->willExecuteWriteAction();
|
||||
$this->willExecuteWriteAction($_SERVER["REQUEST_METHOD"] === "POST");
|
||||
$this->assertNoCSRF();
|
||||
|
||||
$photo = $this->photos->getByOwnerAndVID($ownerId, $photoId);
|
||||
if(!$photo) $this->notFound();
|
||||
if(is_null($this->user) || $this->user->id != $ownerId)
|
||||
$this->flashFail("err", "Ошибка доступа", "Недостаточно прав для модификации данного ресурса.");
|
||||
|
||||
$this->flashFail("err", tr("error_access_denied_short"), tr("error_access_denied"));
|
||||
|
||||
if(!is_null($album = $photo->getAlbum()))
|
||||
$redirect = $album->getOwner() instanceof User ? "/id0" : "/club" . $ownerId;
|
||||
else
|
||||
$redirect = "/id0";
|
||||
|
||||
$photo->isolate();
|
||||
$photo->delete();
|
||||
|
||||
$this->flash("succ", "Фотография удалена", "Эта фотография была успешно удалена.");
|
||||
$this->redirect("/id0");
|
||||
if($_SERVER["REQUEST_METHOD"] === "POST")
|
||||
$this->returnJson(["success" => true]);
|
||||
|
||||
$this->flash("succ", tr("photo_is_deleted"), tr("photo_is_deleted_desc"));
|
||||
$this->redirect($redirect);
|
||||
}
|
||||
}
|
||||
|
|
151
Web/Presenters/ReportPresenter.php
Normal file
151
Web/Presenters/ReportPresenter.php
Normal file
|
@ -0,0 +1,151 @@
|
|||
<?php declare(strict_types=1);
|
||||
namespace openvk\Web\Presenters;
|
||||
use openvk\Web\Models\Repositories\Users;
|
||||
use openvk\Web\Models\Repositories\Reports;
|
||||
use openvk\Web\Models\Repositories\Posts;
|
||||
use openvk\Web\Models\Entities\Report;
|
||||
|
||||
final class ReportPresenter extends OpenVKPresenter
|
||||
{
|
||||
private $reports;
|
||||
|
||||
function __construct(Reports $reports)
|
||||
{
|
||||
$this->reports = $reports;
|
||||
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
function renderList(): void
|
||||
{
|
||||
$this->assertUserLoggedIn();
|
||||
$this->assertPermission('openvk\Web\Models\Entities\TicketReply', 'write', 0);
|
||||
if ($_SERVER["REQUEST_METHOD"] === "POST")
|
||||
$this->assertNoCSRF();
|
||||
|
||||
$act = in_array($this->queryParam("act"), ["post", "photo", "video", "group", "comment", "note", "app", "user"]) ? $this->queryParam("act") : NULL;
|
||||
|
||||
if (!$this->queryParam("orig")) {
|
||||
$this->template->reports = $this->reports->getReports(0, (int)($this->queryParam("p") ?? 1), $act, $_SERVER["REQUEST_METHOD"] !== "POST");
|
||||
$this->template->count = $this->reports->getReportsCount();
|
||||
} else {
|
||||
$orig = $this->reports->get((int) $this->queryParam("orig"));
|
||||
if (!$orig) $this->redirect("/scumfeed");
|
||||
|
||||
$this->template->reports = $orig->getDuplicates();
|
||||
$this->template->count = $orig->getDuplicatesCount();
|
||||
$this->template->orig = $orig->getId();
|
||||
}
|
||||
$this->template->paginatorConf = (object) [
|
||||
"count" => $this->template->count,
|
||||
"page" => $this->queryParam("p") ?? 1,
|
||||
"amount" => NULL,
|
||||
"perPage" => 15,
|
||||
];
|
||||
$this->template->mode = $act ?? "all";
|
||||
|
||||
if ($_SERVER["REQUEST_METHOD"] === "POST") {
|
||||
$reports = [];
|
||||
foreach ($this->reports->getReports(0, 0, $act, false) as $report) {
|
||||
$reports[] = [
|
||||
"id" => $report->getId(),
|
||||
"author" => [
|
||||
"id" => $report->getReportAuthor()->getId(),
|
||||
"url" => $report->getReportAuthor()->getURL(),
|
||||
"name" => $report->getReportAuthor()->getCanonicalName(),
|
||||
"is_female" => $report->getReportAuthor()->isFemale()
|
||||
],
|
||||
"content" => [
|
||||
"name" => $report->getContentName(),
|
||||
"type" => $report->getContentType(),
|
||||
"id" => $report->getContentId(),
|
||||
"url" => $report->getContentType() === "user" ? (new Users)->get((int) $report->getContentId())->getURL() : NULL
|
||||
],
|
||||
"duplicates" => $report->getDuplicatesCount(),
|
||||
];
|
||||
}
|
||||
$this->returnJson(["reports" => $reports]);
|
||||
}
|
||||
}
|
||||
|
||||
function renderView(int $id): void
|
||||
{
|
||||
$this->assertUserLoggedIn();
|
||||
$this->assertPermission('openvk\Web\Models\Entities\TicketReply', 'write', 0);
|
||||
|
||||
$report = $this->reports->get($id);
|
||||
if(!$report || $report->isDeleted())
|
||||
$this->notFound();
|
||||
|
||||
$this->template->report = $report;
|
||||
}
|
||||
|
||||
function renderCreate(int $id): void
|
||||
{
|
||||
$this->assertUserLoggedIn();
|
||||
$this->willExecuteWriteAction();
|
||||
|
||||
if(!$id)
|
||||
exit(json_encode([ "error" => tr("error_segmentation") ]));
|
||||
|
||||
if(in_array($this->queryParam("type"), ["post", "photo", "video", "group", "comment", "note", "app", "user"])) {
|
||||
if (count(iterator_to_array($this->reports->getDuplicates($this->queryParam("type"), $id, NULL, $this->user->id))) <= 0) {
|
||||
$report = new Report;
|
||||
$report->setUser_id($this->user->id);
|
||||
$report->setTarget_id($id);
|
||||
$report->setType($this->queryParam("type"));
|
||||
$report->setReason($this->queryParam("reason"));
|
||||
$report->setCreated(time());
|
||||
$report->save();
|
||||
}
|
||||
|
||||
exit(json_encode([ "reason" => $this->queryParam("reason") ]));
|
||||
} else {
|
||||
exit(json_encode([ "error" => "Unable to submit a report on this content type" ]));
|
||||
}
|
||||
}
|
||||
|
||||
function renderAction(int $id): void
|
||||
{
|
||||
$this->assertUserLoggedIn();
|
||||
$this->willExecuteWriteAction();
|
||||
$this->assertPermission('openvk\Web\Models\Entities\TicketReply', 'write', 0);
|
||||
|
||||
$report = $this->reports->get($id);
|
||||
if(!$report || $report->isDeleted()) $this->notFound();
|
||||
|
||||
if ($this->postParam("ban")) {
|
||||
$report->deleteContent();
|
||||
$report->banUser($this->user->identity->getId());
|
||||
|
||||
$this->flash("suc", tr("death"), tr("user_successfully_banned"));
|
||||
} else if ($this->postParam("delete")) {
|
||||
$report->deleteContent();
|
||||
|
||||
$this->flash("suc", tr("nehay"), tr("content_is_deleted"));
|
||||
} else if ($this->postParam("ignore")) {
|
||||
$report->delete();
|
||||
|
||||
$this->flash("suc", tr("nehay"), tr("report_is_ignored"));
|
||||
} else if ($this->postParam("banClubOwner") || $this->postParam("banClub")) {
|
||||
if ($report->getContentType() !== "group")
|
||||
$this->flashFail("err", tr("error_access_denied_short"), tr("error_access_denied"));
|
||||
|
||||
$club = $report->getContentObject();
|
||||
if (!$club || $club->isBanned())
|
||||
$this->flashFail("err", tr("error_access_denied_short"), tr("error_access_denied"));
|
||||
|
||||
if ($this->postParam("banClubOwner")) {
|
||||
$club->getOwner()->ban("**content-" . $report->getContentType() . "-" . $report->getContentId() . "**", false, $club->getOwner()->getNewBanTime(), $this->user->identity->getId());
|
||||
} else {
|
||||
$club->ban("**content-" . $report->getContentType() . "-" . $report->getContentId() . "**");
|
||||
}
|
||||
|
||||
$report->delete();
|
||||
|
||||
$this->flash("suc", tr("death"), ($this->postParam("banClubOwner") ? tr("group_owner_is_banned") : tr("group_is_banned")));
|
||||
}
|
||||
|
||||
$this->redirect("/scumfeed");
|
||||
}
|
||||
}
|
|
@ -1,18 +1,28 @@
|
|||
<?php declare(strict_types=1);
|
||||
namespace openvk\Web\Presenters;
|
||||
use openvk\Web\Models\Entities\{User, Club};
|
||||
use openvk\Web\Models\Repositories\{Users, Clubs};
|
||||
use openvk\Web\Models\Repositories\{Users, Clubs, Posts, Comments, Videos, Applications, Notes};
|
||||
use Chandler\Database\DatabaseConnection;
|
||||
|
||||
final class SearchPresenter extends OpenVKPresenter
|
||||
{
|
||||
private $users;
|
||||
private $clubs;
|
||||
private $posts;
|
||||
private $comments;
|
||||
private $videos;
|
||||
private $apps;
|
||||
private $notes;
|
||||
|
||||
function __construct(Users $users, Clubs $clubs)
|
||||
{
|
||||
$this->users = $users;
|
||||
$this->clubs = $clubs;
|
||||
$this->users = $users;
|
||||
$this->clubs = $clubs;
|
||||
$this->posts = new Posts;
|
||||
$this->comments = new Comments;
|
||||
$this->videos = new Videos;
|
||||
$this->apps = new Applications;
|
||||
$this->notes = new Notes;
|
||||
|
||||
parent::__construct();
|
||||
}
|
||||
|
@ -21,6 +31,8 @@ final class SearchPresenter extends OpenVKPresenter
|
|||
{
|
||||
$query = $this->queryParam("query") ?? "";
|
||||
$type = $this->queryParam("type") ?? "users";
|
||||
$sorter = $this->queryParam("sort") ?? "id";
|
||||
$invert = $this->queryParam("invert") == 1 ? "ASC" : "DESC";
|
||||
$page = (int) ($this->queryParam("p") ?? 1);
|
||||
|
||||
$this->willExecuteWriteAction();
|
||||
|
@ -28,11 +40,58 @@ final class SearchPresenter extends OpenVKPresenter
|
|||
$this->assertUserLoggedIn();
|
||||
|
||||
# https://youtu.be/pSAWM5YuXx8
|
||||
|
||||
$repos = [ "groups" => "clubs", "users" => "users" ];
|
||||
|
||||
$repos = [
|
||||
"groups" => "clubs",
|
||||
"users" => "users",
|
||||
"posts" => "posts",
|
||||
"comments" => "comments",
|
||||
"videos" => "videos",
|
||||
"audios" => "posts",
|
||||
"apps" => "apps",
|
||||
"notes" => "notes"
|
||||
];
|
||||
|
||||
switch($sorter) {
|
||||
default:
|
||||
case "id":
|
||||
$sort = "id " . $invert;
|
||||
break;
|
||||
case "name":
|
||||
$sort = "first_name " . $invert;
|
||||
break;
|
||||
case "rating":
|
||||
$sort = "rating " . $invert;
|
||||
break;
|
||||
}
|
||||
|
||||
$parameters = [
|
||||
"type" => $this->queryParam("type"),
|
||||
"city" => $this->queryParam("city") != "" ? $this->queryParam("city") : NULL,
|
||||
"maritalstatus" => $this->queryParam("maritalstatus") != 0 ? $this->queryParam("maritalstatus") : NULL,
|
||||
"with_photo" => $this->queryParam("with_photo"),
|
||||
"status" => $this->queryParam("status") != "" ? $this->queryParam("status") : NULL,
|
||||
"politViews" => $this->queryParam("politViews") != 0 ? $this->queryParam("politViews") : NULL,
|
||||
"email" => $this->queryParam("email"),
|
||||
"telegram" => $this->queryParam("telegram"),
|
||||
"site" => $this->queryParam("site") != "" ? "https://".$this->queryParam("site") : NULL,
|
||||
"address" => $this->queryParam("address"),
|
||||
"is_online" => $this->queryParam("is_online") == 1 ? 1 : NULL,
|
||||
"interests" => $this->queryParam("interests") != "" ? $this->queryParam("interests") : NULL,
|
||||
"fav_mus" => $this->queryParam("fav_mus") != "" ? $this->queryParam("fav_mus") : NULL,
|
||||
"fav_films" => $this->queryParam("fav_films") != "" ? $this->queryParam("fav_films") : NULL,
|
||||
"fav_shows" => $this->queryParam("fav_shows") != "" ? $this->queryParam("fav_shows") : NULL,
|
||||
"fav_books" => $this->queryParam("fav_books") != "" ? $this->queryParam("fav_books") : NULL,
|
||||
"fav_quote" => $this->queryParam("fav_quote") != "" ? $this->queryParam("fav_quote") : NULL,
|
||||
"hometown" => $this->queryParam("hometown") != "" ? $this->queryParam("hometown") : NULL,
|
||||
"before" => $this->queryParam("datebefore") != "" ? strtotime($this->queryParam("datebefore")) : NULL,
|
||||
"after" => $this->queryParam("dateafter") != "" ? strtotime($this->queryParam("dateafter")) : NULL,
|
||||
"gender" => $this->queryParam("gender") != "" && $this->queryParam("gender") != 2 ? $this->queryParam("gender") : NULL
|
||||
];
|
||||
|
||||
$repo = $repos[$type] or $this->throwError(400, "Bad Request", "Invalid search entity $type.");
|
||||
|
||||
$results = $this->{$repo}->find($query);
|
||||
$results = $this->{$repo}->find($query, $parameters, $sort);
|
||||
$iterator = $results->page($page);
|
||||
$count = $results->size();
|
||||
|
||||
|
|
|
@ -385,7 +385,7 @@ final class SupportPresenter extends OpenVKPresenter
|
|||
$agent->setNumerate((int) $this->postParam("number") ?? NULL);
|
||||
$agent->setIcon($this->postParam("avatar"));
|
||||
$agent->save();
|
||||
$this->flashFail("succ", "Успех", "Профиль отредактирован.");
|
||||
$this->flashFail("succ", tr("agent_profile_edited"));
|
||||
} else {
|
||||
$agent = new SupportAgent;
|
||||
$agent->setAgent($this->user->identity->getId());
|
||||
|
@ -393,7 +393,27 @@ final class SupportPresenter extends OpenVKPresenter
|
|||
$agent->setNumerate((int) $this->postParam("number") ?? NULL);
|
||||
$agent->setIcon($this->postParam("avatar"));
|
||||
$agent->save();
|
||||
$this->flashFail("succ", "Успех", "Профиль создан. Теперь пользователи видят Ваши псевдоним и аватарку вместо стандартных аватарки и номера.");
|
||||
$this->flashFail("succ", tr("agent_profile_created_1"), tr("agent_profile_created_2"));
|
||||
}
|
||||
}
|
||||
|
||||
function renderCloseTicket(int $id): void
|
||||
{
|
||||
$this->assertUserLoggedIn();
|
||||
$this->assertNoCSRF();
|
||||
$this->willExecuteWriteAction();
|
||||
|
||||
$ticket = $this->tickets->get($id);
|
||||
|
||||
if($ticket->isDeleted() === 1 || $ticket->getType() === 2 || $ticket->getUserId() !== $this->user->id) {
|
||||
header("HTTP/1.1 403 Forbidden");
|
||||
header("Location: /support/view/" . $id);
|
||||
exit;
|
||||
}
|
||||
|
||||
$ticket->setType(2);
|
||||
$ticket->save();
|
||||
|
||||
$this->flashFail("succ", tr("ticket_changed"), tr("ticket_changed_comment"));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -84,6 +84,9 @@ final class TopicsPresenter extends OpenVKPresenter
|
|||
if($this->postParam("as_group") === "on" && $club->canBeModifiedBy($this->user->identity))
|
||||
$flags |= 0b10000000;
|
||||
|
||||
if($_FILES["_vid_attachment"] && OPENVK_ROOT_CONF['openvk']['preferences']['videos']['disableUploading'])
|
||||
$this->flashFail("err", tr("error"), "Video uploads are disabled by the system administrator.");
|
||||
|
||||
$topic = new Topic;
|
||||
$topic->setGroup($club->getId());
|
||||
$topic->setOwner($this->user->id);
|
||||
|
@ -105,10 +108,10 @@ final class TopicsPresenter extends OpenVKPresenter
|
|||
}
|
||||
|
||||
if($_FILES["_vid_attachment"]["error"] === UPLOAD_ERR_OK) {
|
||||
$video = Video::fastMake($this->user->id, $this->postParam("text"), $_FILES["_vid_attachment"]);
|
||||
$video = Video::fastMake($this->user->id, $_FILES["_vid_attachment"]["name"], $this->postParam("text"), $_FILES["_vid_attachment"]);
|
||||
}
|
||||
} catch(ISE $ex) {
|
||||
$this->flash("err", "Не удалось опубликовать комментарий", "Файл медиаконтента повреждён или слишком велик.");
|
||||
$this->flash("err", tr("error_when_publishing_comment"), tr("error_comment_file_too_big"));
|
||||
$this->redirect("/topic" . $topic->getPrettyId());
|
||||
}
|
||||
|
||||
|
@ -123,7 +126,7 @@ final class TopicsPresenter extends OpenVKPresenter
|
|||
$comment->setFlags($flags);
|
||||
$comment->save();
|
||||
} catch (\LengthException $ex) {
|
||||
$this->flash("err", "Не удалось опубликовать комментарий", "Комментарий слишком большой.");
|
||||
$this->flash("err", tr("error_when_publishing_comment"), tr("error_comment_too_big"));
|
||||
$this->redirect("/topic" . $topic->getPrettyId());
|
||||
}
|
||||
|
||||
|
|
|
@ -39,6 +39,7 @@ final class UserPresenter extends OpenVKPresenter
|
|||
}
|
||||
} else {
|
||||
$this->template->albums = (new Albums)->getUserAlbums($user);
|
||||
$this->template->avatarAlbum = (new Albums)->getUserAvatarAlbum($user);
|
||||
$this->template->albumsCount = (new Albums)->getUserAlbumsCount($user);
|
||||
$this->template->videos = (new Videos)->getByUser($user, 1, 2);
|
||||
$this->template->videosCount = (new Videos)->getUserVideosCount($user);
|
||||
|
@ -71,7 +72,7 @@ final class UserPresenter extends OpenVKPresenter
|
|||
if(!is_null($this->user)) {
|
||||
if($this->template->mode !== "friends" && $this->user->id !== $id) {
|
||||
$name = $user->getFullName();
|
||||
$this->flash("err", "Ошибка доступа", "Вы не можете просматривать полный список подписок $name.");
|
||||
$this->flash("err", tr("error_access_denied_short"), tr("error_viewing_subs", $name));
|
||||
|
||||
$this->redirect($user->getURL());
|
||||
}
|
||||
|
@ -106,11 +107,11 @@ final class UserPresenter extends OpenVKPresenter
|
|||
$this->notFound();
|
||||
|
||||
if(!$club->canBeModifiedBy($this->user->identity ?? NULL))
|
||||
$this->flashFail("err", "Ошибка доступа", "У вас недостаточно прав, чтобы изменять этот ресурс.", NULL, true);
|
||||
$this->flashFail("err", tr("error_access_denied_short"), tr("error_access_denied"), NULL, true);
|
||||
|
||||
$isClubPinned = $this->user->identity->isClubPinned($club);
|
||||
if(!$isClubPinned && $this->user->identity->getPinnedClubCount() > 10)
|
||||
$this->flashFail("err", "Ошибка", "Находится в левом меню могут максимум 10 групп", NULL, true);
|
||||
$this->flashFail("err", tr("error"), tr("error_max_pinned_clubs"), NULL, true);
|
||||
|
||||
if($club->getOwner()->getId() === $this->user->identity->getId()) {
|
||||
$club->setOwner_Club_Pinned(!$isClubPinned);
|
||||
|
@ -236,7 +237,7 @@ final class UserPresenter extends OpenVKPresenter
|
|||
} elseif($_GET['act'] === "status") {
|
||||
if(mb_strlen($this->postParam("status")) > 255) {
|
||||
$statusLength = (string) mb_strlen($this->postParam("status"));
|
||||
$this->flashFail("err", "Ошибка", "Статус слишком длинный ($statusLength символов вместо 255 символов)", NULL, true);
|
||||
$this->flashFail("err", tr("error"), tr("error_status_too_long", $statusLength), NULL, true);
|
||||
}
|
||||
|
||||
$user->setStatus(empty($this->postParam("status")) ? NULL : $this->postParam("status"));
|
||||
|
@ -280,7 +281,7 @@ final class UserPresenter extends OpenVKPresenter
|
|||
|
||||
if($_SERVER["REQUEST_METHOD"] === "POST") {
|
||||
if(!$user->verifyNumber($this->postParam("code") ?? 0))
|
||||
$this->flashFail("err", "Ошибка", "Не удалось подтвердить номер телефона: неверный код.");
|
||||
$this->flashFail("err", tr("error"), tr("invalid_code"));
|
||||
|
||||
$this->flash("succ", tr("changes_saved"), tr("changes_saved_comment"));
|
||||
}
|
||||
|
@ -301,7 +302,7 @@ final class UserPresenter extends OpenVKPresenter
|
|||
$this->redirect($user->getURL());
|
||||
}
|
||||
|
||||
function renderSetAvatar(): void
|
||||
function renderSetAvatar()
|
||||
{
|
||||
$this->assertUserLoggedIn();
|
||||
$this->willExecuteWriteAction();
|
||||
|
@ -321,8 +322,26 @@ final class UserPresenter extends OpenVKPresenter
|
|||
$album->addPhoto($photo);
|
||||
$album->setEdited(time());
|
||||
$album->save();
|
||||
|
||||
$this->flashFail("succ", tr("photo_saved"), tr("photo_saved_comment"));
|
||||
|
||||
$flags = 0;
|
||||
$flags |= 0b00010000;
|
||||
|
||||
$post = new Post;
|
||||
$post->setOwner($this->user->id);
|
||||
$post->setWall($this->user->id);
|
||||
$post->setCreated(time());
|
||||
$post->setContent("");
|
||||
$post->setFlags($flags);
|
||||
$post->save();
|
||||
$post->attach($photo);
|
||||
if($this->postParam("ava", true) == (int)1) {
|
||||
$this->returnJson([
|
||||
"url" => $photo->getURL(),
|
||||
"id" => $photo->getPrettyId()
|
||||
]);
|
||||
} else {
|
||||
$this->flashFail("succ", tr("photo_saved"), tr("photo_saved_comment"));
|
||||
}
|
||||
}
|
||||
|
||||
function renderSettings(): void
|
||||
|
@ -463,6 +482,7 @@ final class UserPresenter extends OpenVKPresenter
|
|||
"menu_novajoj" => "news",
|
||||
"menu_ligiloj" => "links",
|
||||
"menu_standardo" => "poster",
|
||||
"menu_aplikoj" => "apps"
|
||||
];
|
||||
foreach($settings as $checkbox => $setting)
|
||||
$user->setLeftMenuItemStatus($setting, $this->checkbox($checkbox));
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue