mirror of
https://github.com/openvk/openvk
synced 2025-01-12 19:13:20 +03:00
Merge branch 'master' into anon-comments
This commit is contained in:
commit
13a3e0f169
254 changed files with 12485 additions and 1465 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
|
!themepacks/midnight
|
||||||
storage/*
|
storage/*
|
||||||
!storage/.gitkeep
|
!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>
|
|
36
README.md
36
README.md
|
@ -2,29 +2,25 @@
|
||||||
|
|
||||||
_[Русский](README_RU.md)_
|
_[Русский](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.
|
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?
|
## 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)
|
* `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)
|
* Grab a prebuilt OpenVK distro from [GitHub artifacts](https://nightly.link/openvk/archive/workflows/nightly/master/OpenVK%20Archive.zip)
|
||||||
|
|
||||||
## Instances
|
## Instances
|
||||||
|
|
||||||
* **[openvk.su](https://openvk.su/)**
|
A list of instances can be found in [our wiki of this repository](https://github.com/openvk/openvk/wiki/Instances).
|
||||||
* **[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/)
|
|
||||||
|
|
||||||
## Can I create my own OpenVK instance?
|
## 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).
|
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)
|
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.
|
2. Install MySQL-compatible database.
|
||||||
|
|
||||||
* We recommend using Percona Server, but any MySQL-compatible server should work
|
* 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+ recommended.
|
* 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.
|
* 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:
|
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`
|
* **Password**: `admin`
|
||||||
* It is recommended to change the password of the built-in account or disable it.
|
* 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?
|
### Looking for Docker or Kubernetes deployment?
|
||||||
See `install/automated/docker/README.md` and `install/automated/kubernetes/README.md` for Docker and Kubernetes deployment instructions.
|
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?
|
### 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?
|
## Where can I get assistance?
|
||||||
|
|
||||||
You may reach out to us via:
|
You may reach out to us via:
|
||||||
|
|
||||||
* [Bug-tracker](https://github.com/openvk/openvk/projects/1)
|
* [Bug Tracker](https://github.com/openvk/openvk/projects/1)
|
||||||
* [Ticketing system](https://openvk.su/support?act=new)
|
* [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.
|
* Telegram Chat: Go to [our channel](https://t.me/openvkenglish) and open discussion in our channel menu.
|
||||||
* [Reddit](https://www.reddit.com/r/openvk/)
|
* [Reddit](https://www.reddit.com/r/openvk/)
|
||||||
* [Discussions](https://github.com/openvk/openvk/discussions)
|
* [GitHub Discussions](https://github.com/openvk/openvk/discussions)
|
||||||
* Matrix chat: #openvk:matrix.org
|
* 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">
|
<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">
|
<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)_
|
_[English](README.md)_
|
||||||
|
|
||||||
**OpenVK** - это попытка создать простую CMS, которая ~~косплеит~~ имитирует старый ВКонтакте. На данный момент представленный здесь исходный код проекта пока не является стабильным.
|
**OpenVK** — это попытка создать простую CMS, которая ~~косплеит~~ имитирует старый ВКонтакте. На данный момент, представленный здесь исходный код проекта пока не является стабильным.
|
||||||
|
|
||||||
ВКонтакте принадлежит Павлу Дурову и VK Group.
|
ВКонтакте принадлежит Павлу Дурову и 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/)**
|
Список инстанций находится в [нашей вики этого репозитория](https://github.com/openvk/openvk/wiki/Instances-(RU)).
|
||||||
* **[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/)
|
|
||||||
|
|
||||||
## Могу ли я создать свою собственную инстанцию OpenVK?
|
## Могу ли я создать свою собственную инстанцию OpenVK?
|
||||||
|
|
||||||
Да! И всегда пожалуйста.
|
Да! И всегда пожалуйста.
|
||||||
|
|
||||||
Однако, OVK использует Chandler Application Server. Это программное обеспечение требует расширений, которые могут быть не предоставлены вашим хостинг-провайдером (а именно, sodium и yaml. эти расширения доступны на большинстве хостингов ISPManager).
|
Однако, OpenVK использует Chandler Application Server. Это программное обеспечение требует расширений, которые могут быть не предоставлены вашим хостинг-провайдером (а именно, sodium и yaml. Эти расширения доступны на большинстве хостингов ISPManager).
|
||||||
|
|
||||||
Если вы хотите, вы можете добавить вашу инстанцию в список выше, чтобы люди могли зарегистрироваться там.
|
Если хотите, вы можете добавить вашу инстанцию в список выше, чтобы люди могли зарегистрироваться там.
|
||||||
|
|
||||||
### Процедура установки
|
### Процедура установки
|
||||||
|
|
||||||
|
@ -38,7 +34,7 @@ _[English](README.md)_
|
||||||
|
|
||||||
2. Установите MySQL-совместимую базу данных.
|
2. Установите MySQL-совместимую базу данных.
|
||||||
|
|
||||||
* Мы рекомендуем использовать Persona Server, но любая MySQL-совместимая база данных должна работать
|
* Мы рекомендуем использовать Persona Server, но любая MySQL-совместимая база данных должна работать.
|
||||||
* Сервер должен поддерживать хотя бы MySQL 5.6, рекомендуется использовать MySQL 8.0+.
|
* Сервер должен поддерживать хотя бы MySQL 5.6, рекомендуется использовать MySQL 8.0+.
|
||||||
* Поддержка для MySQL 4.1+ находится в процессе, а пока замените `utf8mb4` и `utf8mb4_unicode_520_ci` на `utf8` и `utf8_unicode_ci` в SQL-файлах, соответственно.
|
* Поддержка для 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`
|
* **Пароль**: `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
|
# Установка в Docker/Kubernetes
|
||||||
Подробные иструкции можно найти в `install/automated/docker/README.md` и `install/automated/kubernetes/README.md` соответственно.
|
Подробные иструкции можно найти в `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)
|
* [Помощь в OVK](https://openvk.su/support?act=new)
|
||||||
* Telegram-чат: Перейдите на [наш канал](https://t.me/openvk) и откройте обсуждение в меню нашего канала.
|
* Telegram-чат: Перейдите на [наш канал](https://t.me/openvk) и откройте обсуждение в меню нашего канала.
|
||||||
* [Reddit](https://www.reddit.com/r/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:matrix.org
|
||||||
|
|
||||||
**Внимание**: баг-трекер, форум, телеграм- и matrix-чат являются публичными местами, и жалобы в OVK обслуживается волонтерами. Если вам нужно сообщить о чем-то, что не должно быть раскрыто широкой публике (например, сообщение об уязвимости), пожалуйста, свяжитесь с нами напрямую по этому адресу: **openvk [собака] tutanota [точка] com**.
|
**Внимание**: баг-трекер, форум, Telegram- и Matrix-чат являются публичными местами, и жалобы в OVK обслуживается волонтерами. Если вам нужно сообщить о чем-то, что не должно быть раскрыто широкой публике (например, сообщение об уязвимости), пожалуйста, свяжитесь с нами напрямую по этому адресу: **openvk [собачка] tutanota [точка] com**.
|
||||||
|
|
||||||
<a href="https://codeberg.org/OpenVK/openvk">
|
<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">
|
<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(),
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
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,19 @@
|
||||||
namespace openvk\ServiceAPI;
|
namespace openvk\ServiceAPI;
|
||||||
use openvk\Web\Models\Entities\Post;
|
use openvk\Web\Models\Entities\Post;
|
||||||
use openvk\Web\Models\Entities\User;
|
use openvk\Web\Models\Entities\User;
|
||||||
use openvk\Web\Models\Repositories\Posts;
|
use openvk\Web\Models\Repositories\{Posts, Notes};
|
||||||
|
|
||||||
class Wall implements Handler
|
class Wall implements Handler
|
||||||
{
|
{
|
||||||
protected $user;
|
protected $user;
|
||||||
protected $posts;
|
protected $posts;
|
||||||
|
protected $notes;
|
||||||
|
|
||||||
function __construct(?User $user)
|
function __construct(?User $user)
|
||||||
{
|
{
|
||||||
$this->user = $user;
|
$this->user = $user;
|
||||||
$this->posts = new Posts;
|
$this->posts = new Posts;
|
||||||
|
$this->notes = new Notes;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getPost(int $id, callable $resolve, callable $reject): void
|
function getPost(int $id, callable $resolve, callable $reject): void
|
||||||
|
@ -71,4 +73,26 @@ class Wall implements Handler
|
||||||
|
|
||||||
$resolve($post->getId());
|
$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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,13 +45,12 @@ final class Account extends VKAPIRequestHandler
|
||||||
{
|
{
|
||||||
$this->requireUser();
|
$this->requireUser();
|
||||||
|
|
||||||
$this->getUser()->setOnline(time());
|
$this->getUser()->updOnline($this->getPlatform());
|
||||||
$this->getUser()->save();
|
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
function setOffline(): object
|
function setOffline(): int
|
||||||
{
|
{
|
||||||
$this->requireUser();
|
$this->requireUser();
|
||||||
|
|
||||||
|
@ -67,6 +66,8 @@ final class Account extends VKAPIRequestHandler
|
||||||
|
|
||||||
function getCounters(string $filter = ""): object
|
function getCounters(string $filter = ""): object
|
||||||
{
|
{
|
||||||
|
$this->requireUser();
|
||||||
|
|
||||||
return (object) [
|
return (object) [
|
||||||
"friends" => $this->getUser()->getFollowersCount(),
|
"friends" => $this->getUser()->getFollowersCount(),
|
||||||
"notifications" => $this->getUser()->getNotificationsCount(),
|
"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
|
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->requireUser();
|
||||||
|
$this->willExecuteWriteAction();
|
||||||
|
|
||||||
$user = $this->getUser();
|
$user = $this->getUser();
|
||||||
|
|
||||||
$output = [
|
$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
|
function add(string $user_id): int
|
||||||
{
|
{
|
||||||
$this->requireUser();
|
$this->requireUser();
|
||||||
|
$this->willExecuteWriteAction();
|
||||||
|
|
||||||
$users = new UsersRepo;
|
$users = new UsersRepo;
|
||||||
$user = $users->get(intval($user_id));
|
$user = $users->get(intval($user_id));
|
||||||
|
@ -96,6 +97,7 @@ final class Friends extends VKAPIRequestHandler
|
||||||
function delete(string $user_id): int
|
function delete(string $user_id): int
|
||||||
{
|
{
|
||||||
$this->requireUser();
|
$this->requireUser();
|
||||||
|
$this->willExecuteWriteAction();
|
||||||
|
|
||||||
$users = new UsersRepo;
|
$users = new UsersRepo;
|
||||||
|
|
||||||
|
@ -152,10 +154,7 @@ final class Friends extends VKAPIRequestHandler
|
||||||
$response = $followers;
|
$response = $followers;
|
||||||
$usersApi = new Users($this->getUser());
|
$usersApi = new Users($this->getUser());
|
||||||
|
|
||||||
if($extended == 1)
|
$response = $usersApi->get(implode(',', $followers), $fields, 0, $count);
|
||||||
$response = $usersApi->get(implode(',', $followers), $fields, 0, $count);
|
|
||||||
else
|
|
||||||
$response = $usersApi->get(implode(',', $followers), "", 0, $count);
|
|
||||||
|
|
||||||
foreach($response as $user)
|
foreach($response as $user)
|
||||||
$user->user_id = $user->id;
|
$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;
|
namespace openvk\VKAPI\Handlers;
|
||||||
use openvk\Web\Models\Repositories\Clubs as ClubsRepo;
|
use openvk\Web\Models\Repositories\Clubs as ClubsRepo;
|
||||||
use openvk\Web\Models\Repositories\Users as UsersRepo;
|
use openvk\Web\Models\Repositories\Users as UsersRepo;
|
||||||
|
use openvk\Web\Models\Entities\Club;
|
||||||
|
|
||||||
final class Groups extends VKAPIRequestHandler
|
final class Groups extends VKAPIRequestHandler
|
||||||
{
|
{
|
||||||
|
@ -185,7 +186,7 @@ final class Groups extends VKAPIRequestHandler
|
||||||
$response[$i]->site = $clb->getWebsite();
|
$response[$i]->site = $clb->getWebsite();
|
||||||
break;
|
break;
|
||||||
case "description":
|
case "description":
|
||||||
$response[$i]->desctiption = $clb->getDescription();
|
$response[$i]->description = $clb->getDescription();
|
||||||
break;
|
break;
|
||||||
case "contacts":
|
case "contacts":
|
||||||
$contacts;
|
$contacts;
|
||||||
|
@ -237,6 +238,7 @@ final class Groups extends VKAPIRequestHandler
|
||||||
function join(int $group_id)
|
function join(int $group_id)
|
||||||
{
|
{
|
||||||
$this->requireUser();
|
$this->requireUser();
|
||||||
|
$this->willExecuteWriteAction();
|
||||||
|
|
||||||
$club = (new ClubsRepo)->get($group_id);
|
$club = (new ClubsRepo)->get($group_id);
|
||||||
|
|
||||||
|
@ -251,6 +253,7 @@ final class Groups extends VKAPIRequestHandler
|
||||||
function leave(int $group_id)
|
function leave(int $group_id)
|
||||||
{
|
{
|
||||||
$this->requireUser();
|
$this->requireUser();
|
||||||
|
$this->willExecuteWriteAction();
|
||||||
|
|
||||||
$club = (new ClubsRepo)->get($group_id);
|
$club = (new ClubsRepo)->get($group_id);
|
||||||
|
|
||||||
|
@ -261,4 +264,272 @@ final class Groups extends VKAPIRequestHandler
|
||||||
|
|
||||||
return 1;
|
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
|
function add(string $type, int $owner_id, int $item_id): object
|
||||||
{
|
{
|
||||||
$this->requireUser();
|
$this->requireUser();
|
||||||
|
$this->willExecuteWriteAction();
|
||||||
|
|
||||||
switch($type) {
|
switch($type) {
|
||||||
case "post":
|
case "post":
|
||||||
|
@ -28,6 +29,7 @@ final class Likes extends VKAPIRequestHandler
|
||||||
function delete(string $type, int $owner_id, int $item_id): object
|
function delete(string $type, int $owner_id, int $item_id): object
|
||||||
{
|
{
|
||||||
$this->requireUser();
|
$this->requireUser();
|
||||||
|
$this->willExecuteWriteAction();
|
||||||
|
|
||||||
switch($type) {
|
switch($type) {
|
||||||
case "post":
|
case "post":
|
||||||
|
@ -52,11 +54,7 @@ final class Likes extends VKAPIRequestHandler
|
||||||
case "post":
|
case "post":
|
||||||
$user = (new UsersRepo)->get($user_id);
|
$user = (new UsersRepo)->get($user_id);
|
||||||
if (is_null($user))
|
if (is_null($user))
|
||||||
return (object) [
|
$this->fail(100, "One of the parameters specified was missing or invalid: user not found");
|
||||||
"liked" => 0,
|
|
||||||
"copied" => 0,
|
|
||||||
"sex" => 0
|
|
||||||
];
|
|
||||||
|
|
||||||
$post = (new PostsRepo)->getPostById($owner_id, $item_id);
|
$post = (new PostsRepo)->getPostById($owner_id, $item_id);
|
||||||
if (is_null($post))
|
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->requireUser();
|
||||||
|
$this->willExecuteWriteAction();
|
||||||
|
|
||||||
|
if($forGodSakePleaseDoNotReportAboutMyOnlineActivity == 0)
|
||||||
|
{
|
||||||
|
$this->getUser()->updOnline($this->getPlatform());
|
||||||
|
}
|
||||||
|
|
||||||
if($chat_id !== -1)
|
if($chat_id !== -1)
|
||||||
$this->fail(946, "Chats are not implemented");
|
$this->fail(946, "Chats are not implemented");
|
||||||
else if($sticker_id !== -1)
|
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
|
function delete(string $message_ids, int $spam = 0, int $delete_for_all = 0): object
|
||||||
{
|
{
|
||||||
$this->requireUser();
|
$this->requireUser();
|
||||||
|
$this->willExecuteWriteAction();
|
||||||
|
|
||||||
$msgs = new MSGRepo;
|
$msgs = new MSGRepo;
|
||||||
$ids = preg_split("%, ?%", $message_ids);
|
$ids = preg_split("%, ?%", $message_ids);
|
||||||
|
@ -136,6 +143,7 @@ final class Messages extends VKAPIRequestHandler
|
||||||
function restore(int $message_id): int
|
function restore(int $message_id): int
|
||||||
{
|
{
|
||||||
$this->requireUser();
|
$this->requireUser();
|
||||||
|
$this->willExecuteWriteAction();
|
||||||
|
|
||||||
$msg = (new MSGRepo)->get($message_id);
|
$msg = (new MSGRepo)->get($message_id);
|
||||||
if(!$msg)
|
if(!$msg)
|
||||||
|
|
|
@ -7,9 +7,14 @@ use openvk\VKAPI\Handlers\Wall;
|
||||||
|
|
||||||
final class Newsfeed extends VKAPIRequestHandler
|
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();
|
$this->requireUser();
|
||||||
|
|
||||||
|
if($forGodSakePleaseDoNotReportAboutMyOnlineActivity == 0)
|
||||||
|
{
|
||||||
|
$this->getUser()->updOnline($this->getPlatform());
|
||||||
|
}
|
||||||
|
|
||||||
$id = $this->getUser()->getId();
|
$id = $this->getUser()->getId();
|
||||||
$subs = DatabaseConnection::i()
|
$subs = DatabaseConnection::i()
|
||||||
|
@ -28,6 +33,8 @@ final class Newsfeed extends VKAPIRequestHandler
|
||||||
->where("wall IN (?)", $ids)
|
->where("wall IN (?)", $ids)
|
||||||
->where("deleted", 0)
|
->where("deleted", 0)
|
||||||
->where("id < (?)", empty($start_from) ? PHP_INT_MAX : $start_from)
|
->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");
|
->order("created DESC");
|
||||||
|
|
||||||
$rposts = [];
|
$rposts = [];
|
||||||
|
@ -40,7 +47,7 @@ final class Newsfeed extends VKAPIRequestHandler
|
||||||
return $response;
|
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();
|
$this->requireUser();
|
||||||
|
|
||||||
|
@ -50,7 +57,9 @@ final class Newsfeed extends VKAPIRequestHandler
|
||||||
$queryBase .= " AND `nsfw` = 0";
|
$queryBase .= " AND `nsfw` = 0";
|
||||||
|
|
||||||
$start_from = empty($start_from) ? PHP_INT_MAX : $start_from;
|
$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 = [];
|
$rposts = [];
|
||||||
$ids = [];
|
$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) {
|
||||||
|
$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\InvalidStateException;
|
||||||
use Nette\Utils\ImageException;
|
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\Albums;
|
||||||
|
use openvk\Web\Models\Repositories\Photos as PhotosRepo;
|
||||||
use openvk\Web\Models\Repositories\Clubs;
|
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
|
final class Photos extends VKAPIRequestHandler
|
||||||
{
|
{
|
||||||
|
@ -58,7 +61,7 @@ final class Photos extends VKAPIRequestHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
return (object) [
|
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,
|
"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)
|
function addVote(int $poll_id, string $answers_ids)
|
||||||
{
|
{
|
||||||
$this->requireUser();
|
$this->requireUser();
|
||||||
|
$this->willExecuteWriteAction();
|
||||||
|
|
||||||
$poll = (new PollsRepo)->get($poll_id);
|
$poll = (new PollsRepo)->get($poll_id);
|
||||||
|
|
||||||
|
@ -74,7 +75,7 @@ final class Polls extends VKAPIRequestHandler
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$poll->vote($this->getUser(), explode(",", $answers_ids));
|
$poll->vote($this->getUser(), explode(",", $answers_ids));
|
||||||
return 0;
|
return 1;
|
||||||
} catch(AlreadyVotedException $ex) {
|
} catch(AlreadyVotedException $ex) {
|
||||||
return 0;
|
return 0;
|
||||||
} catch(PollLockedException $ex) {
|
} catch(PollLockedException $ex) {
|
||||||
|
@ -87,6 +88,7 @@ final class Polls extends VKAPIRequestHandler
|
||||||
function deleteVote(int $poll_id)
|
function deleteVote(int $poll_id)
|
||||||
{
|
{
|
||||||
$this->requireUser();
|
$this->requireUser();
|
||||||
|
$this->willExecuteWriteAction();
|
||||||
|
|
||||||
$poll = (new PollsRepo)->get($poll_id);
|
$poll = (new PollsRepo)->get($poll_id);
|
||||||
|
|
||||||
|
@ -95,7 +97,7 @@ final class Polls extends VKAPIRequestHandler
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$poll->revokeVote($this->getUser());
|
$poll->revokeVote($this->getUser());
|
||||||
return 0;
|
return 1;
|
||||||
} catch(PollLockedException $ex) {
|
} catch(PollLockedException $ex) {
|
||||||
$this->fail(15, "Access denied: Poll is locked or isn't revotable");
|
$this->fail(15, "Access denied: Poll is locked or isn't revotable");
|
||||||
} catch(InvalidOptionException $ex) {
|
} 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",
|
"first_name" => "DELETED",
|
||||||
"last_name" => "",
|
"last_name" => "",
|
||||||
"deactivated" => "deleted"
|
"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 if($usrs[$i] == NULL) {
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
@ -108,11 +116,31 @@ final class Users extends VKAPIRequestHandler
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case "last_seen":
|
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) [
|
$response[$i]->last_seen = (object) [
|
||||||
"platform" => 1,
|
"platform" => $platform,
|
||||||
"time" => $usr->getOnline()->timestamp()
|
"time" => $usr->getOnline()->timestamp()
|
||||||
];
|
];
|
||||||
|
}
|
||||||
case "music":
|
case "music":
|
||||||
$response[$i]->music = $usr->getFavoriteMusic();
|
$response[$i]->music = $usr->getFavoriteMusic();
|
||||||
break;
|
break;
|
||||||
|
@ -130,7 +158,10 @@ final class Users extends VKAPIRequestHandler
|
||||||
break;
|
break;
|
||||||
case "interests":
|
case "interests":
|
||||||
$response[$i]->interests = $usr->getInterests();
|
$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;
|
$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 = [];
|
$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)
|
foreach ($find as $user)
|
||||||
$array[] = $user->getId();
|
$array[] = $user->getId();
|
||||||
|
|
||||||
return (object) [
|
return (object) [
|
||||||
"count" => $find->size(),
|
"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);
|
<?php declare(strict_types=1);
|
||||||
namespace openvk\VKAPI\Handlers;
|
namespace openvk\VKAPI\Handlers;
|
||||||
|
use openvk\Web\Models\Repositories\{Users, Clubs};
|
||||||
|
|
||||||
final class Utils extends VKAPIRequestHandler
|
final class Utils extends VKAPIRequestHandler
|
||||||
{
|
{
|
||||||
|
@ -7,4 +8,39 @@ final class Utils extends VKAPIRequestHandler
|
||||||
{
|
{
|
||||||
return time();
|
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);
|
<?php declare(strict_types=1);
|
||||||
namespace openvk\VKAPI\Handlers;
|
namespace openvk\VKAPI\Handlers;
|
||||||
use openvk\VKAPI\Exceptions\APIErrorException;
|
use openvk\VKAPI\Exceptions\APIErrorException;
|
||||||
|
use openvk\Web\Models\Entities\IP;
|
||||||
use openvk\Web\Models\Entities\User;
|
use openvk\Web\Models\Entities\User;
|
||||||
|
use openvk\Web\Models\Repositories\IPs;
|
||||||
|
|
||||||
abstract class VKAPIRequestHandler
|
abstract class VKAPIRequestHandler
|
||||||
{
|
{
|
||||||
protected $user;
|
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
|
protected function fail(int $code, string $message): void
|
||||||
|
@ -22,6 +26,11 @@ abstract class VKAPIRequestHandler
|
||||||
return $this->user;
|
return $this->user;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function getPlatform(): ?string
|
||||||
|
{
|
||||||
|
return $this->platform;
|
||||||
|
}
|
||||||
|
|
||||||
protected function userAuthorized(): bool
|
protected function userAuthorized(): bool
|
||||||
{
|
{
|
||||||
return !is_null($this->getUser());
|
return !is_null($this->getUser());
|
||||||
|
@ -32,4 +41,19 @@ abstract class VKAPIRequestHandler
|
||||||
if(!$this->userAuthorized())
|
if(!$this->userAuthorized())
|
||||||
$this->fail(5, "User authorization failed: no access_token passed.");
|
$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\Repositories\Posts as PostsRepo;
|
||||||
use openvk\Web\Models\Entities\Comment;
|
use openvk\Web\Models\Entities\Comment;
|
||||||
use openvk\Web\Models\Repositories\Comments as CommentsRepo;
|
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
|
final class Wall extends VKAPIRequestHandler
|
||||||
{
|
{
|
||||||
function get(int $owner_id, string $domain = "", int $offset = 0, int $count = 30, int $extended = 0): object
|
function get(int $owner_id, string $domain = "", int $offset = 0, int $count = 30, int $extended = 0): object
|
||||||
{
|
{
|
||||||
|
$this->requireUser();
|
||||||
|
|
||||||
$posts = new PostsRepo;
|
$posts = new PostsRepo;
|
||||||
|
|
||||||
$items = [];
|
$items = [];
|
||||||
|
@ -42,10 +50,14 @@ final class Wall extends VKAPIRequestHandler
|
||||||
if($attachment instanceof \openvk\Web\Models\Entities\Photo) {
|
if($attachment instanceof \openvk\Web\Models\Entities\Photo) {
|
||||||
if($attachment->isDeleted())
|
if($attachment->isDeleted())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
$attachments[] = $this->getApiPhoto($attachment);
|
$attachments[] = $this->getApiPhoto($attachment);
|
||||||
} else if($attachment instanceof \openvk\Web\Models\Entities\Poll) {
|
} else if($attachment instanceof \openvk\Web\Models\Entities\Poll) {
|
||||||
$attachments[] = $this->getApiPoll($attachment, $this->getUser());
|
$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) {
|
} else if ($attachment instanceof \openvk\Web\Models\Entities\Post) {
|
||||||
$repostAttachments = [];
|
$repostAttachments = [];
|
||||||
|
|
||||||
|
@ -53,7 +65,7 @@ final class Wall extends VKAPIRequestHandler
|
||||||
if($repostAttachment instanceof \openvk\Web\Models\Entities\Photo) {
|
if($repostAttachment instanceof \openvk\Web\Models\Entities\Photo) {
|
||||||
if($repostAttachment->isDeleted())
|
if($repostAttachment->isDeleted())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
$repostAttachments[] = $this->getApiPhoto($repostAttachment);
|
$repostAttachments[] = $this->getApiPhoto($repostAttachment);
|
||||||
/* Рекурсии, сука! Заказывали? */
|
/* Рекурсии, сука! Заказывали? */
|
||||||
}
|
}
|
||||||
|
@ -64,6 +76,17 @@ final class Wall extends VKAPIRequestHandler
|
||||||
else
|
else
|
||||||
$profiles[] = $attachment->getOwner()->getId();
|
$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[] = [
|
$repost[] = [
|
||||||
"id" => $attachment->getVirtualId(),
|
"id" => $attachment->getVirtualId(),
|
||||||
"owner_id" => $attachment->isPostedOnBehalfOfGroup() ? $attachment->getOwner()->getId() * -1 : $attachment->getOwner()->getId(),
|
"owner_id" => $attachment->isPostedOnBehalfOfGroup() ? $attachment->getOwner()->getId() * -1 : $attachment->getOwner()->getId(),
|
||||||
|
@ -72,13 +95,22 @@ final class Wall extends VKAPIRequestHandler
|
||||||
"post_type" => "post",
|
"post_type" => "post",
|
||||||
"text" => $attachment->getText(false),
|
"text" => $attachment->getText(false),
|
||||||
"attachments" => $repostAttachments,
|
"attachments" => $repostAttachments,
|
||||||
"post_source" => [
|
"post_source" => $post_source,
|
||||||
"type" => "vk"
|
|
||||||
],
|
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$post_source = [];
|
||||||
|
|
||||||
|
if($post->getPlatform(true) === NULL) {
|
||||||
|
$post_source = (object)["type" => "vk"];
|
||||||
|
} else {
|
||||||
|
$post_source = (object)[
|
||||||
|
"type" => "api",
|
||||||
|
"platform" => $post->getPlatform(true)
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
$items[] = (object)[
|
$items[] = (object)[
|
||||||
"id" => $post->getVirtualId(),
|
"id" => $post->getVirtualId(),
|
||||||
"from_id" => $from_id,
|
"from_id" => $from_id,
|
||||||
|
@ -93,8 +125,9 @@ final class Wall extends VKAPIRequestHandler
|
||||||
"can_archive" => false, # TODO MAYBE
|
"can_archive" => false, # TODO MAYBE
|
||||||
"is_archived" => false,
|
"is_archived" => false,
|
||||||
"is_pinned" => $post->isPinned(),
|
"is_pinned" => $post->isPinned(),
|
||||||
|
"is_explicit" => $post->isExplicit(),
|
||||||
"attachments" => $attachments,
|
"attachments" => $attachments,
|
||||||
"post_source" => (object)["type" => "vk"],
|
"post_source" => $post_source,
|
||||||
"comments" => (object)[
|
"comments" => (object)[
|
||||||
"count" => $post->getCommentsCount(),
|
"count" => $post->getCommentsCount(),
|
||||||
"can_post" => 1
|
"can_post" => 1
|
||||||
|
@ -110,7 +143,7 @@ final class Wall extends VKAPIRequestHandler
|
||||||
"user_reposted" => 0
|
"user_reposted" => 0
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
|
|
||||||
if ($from_id > 0)
|
if ($from_id > 0)
|
||||||
$profiles[] = $from_id;
|
$profiles[] = $from_id;
|
||||||
else
|
else
|
||||||
|
@ -138,7 +171,8 @@ final class Wall extends VKAPIRequestHandler
|
||||||
"screen_name" => $user->getShortCode(),
|
"screen_name" => $user->getShortCode(),
|
||||||
"photo_50" => $user->getAvatarUrl(),
|
"photo_50" => $user->getAvatarUrl(),
|
||||||
"photo_100" => $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_50" => $group->getAvatarUrl(),
|
||||||
"photo_100" => $group->getAvatarUrl(),
|
"photo_100" => $group->getAvatarUrl(),
|
||||||
"photo_200" => $group->getAvatarUrl(),
|
"photo_200" => $group->getAvatarUrl(),
|
||||||
|
"verified" => $group->isVerified()
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -185,7 +220,7 @@ final class Wall extends VKAPIRequestHandler
|
||||||
foreach($psts as $pst) {
|
foreach($psts as $pst) {
|
||||||
$id = explode("_", $pst);
|
$id = explode("_", $pst);
|
||||||
$post = (new PostsRepo)->getPostById(intval($id[0]), intval($id[1]));
|
$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();
|
$from_id = get_class($post->getOwner()) == "openvk\Web\Models\Entities\Club" ? $post->getOwner()->getId() * (-1) : $post->getOwner()->getId();
|
||||||
$attachments = [];
|
$attachments = [];
|
||||||
$repost = []; // чел высрал семь сигарет 😳 помянем 🕯
|
$repost = []; // чел высрал семь сигарет 😳 помянем 🕯
|
||||||
|
@ -194,6 +229,10 @@ final class Wall extends VKAPIRequestHandler
|
||||||
$attachments[] = $this->getApiPhoto($attachment);
|
$attachments[] = $this->getApiPhoto($attachment);
|
||||||
} else if($attachment instanceof \openvk\Web\Models\Entities\Poll) {
|
} else if($attachment instanceof \openvk\Web\Models\Entities\Poll) {
|
||||||
$attachments[] = $this->getApiPoll($attachment, $user);
|
$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) {
|
} else if ($attachment instanceof \openvk\Web\Models\Entities\Post) {
|
||||||
$repostAttachments = [];
|
$repostAttachments = [];
|
||||||
|
|
||||||
|
@ -201,17 +240,28 @@ final class Wall extends VKAPIRequestHandler
|
||||||
if($repostAttachment instanceof \openvk\Web\Models\Entities\Photo) {
|
if($repostAttachment instanceof \openvk\Web\Models\Entities\Photo) {
|
||||||
if($attachment->isDeleted())
|
if($attachment->isDeleted())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
$repostAttachments[] = $this->getApiPhoto($repostAttachment);
|
$repostAttachments[] = $this->getApiPhoto($repostAttachment);
|
||||||
/* Рекурсии, сука! Заказывали? */
|
/* Рекурсии, сука! Заказывали? */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($attachment->isPostedOnBehalfOfGroup())
|
if ($attachment->isPostedOnBehalfOfGroup())
|
||||||
$groups[] = $attachment->getOwner()->getId();
|
$groups[] = $attachment->getOwner()->getId();
|
||||||
else
|
else
|
||||||
$profiles[] = $attachment->getOwner()->getId();
|
$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[] = [
|
$repost[] = [
|
||||||
"id" => $attachment->getVirtualId(),
|
"id" => $attachment->getVirtualId(),
|
||||||
"owner_id" => $attachment->isPostedOnBehalfOfGroup() ? $attachment->getOwner()->getId() * -1 : $attachment->getOwner()->getId(),
|
"owner_id" => $attachment->isPostedOnBehalfOfGroup() ? $attachment->getOwner()->getId() * -1 : $attachment->getOwner()->getId(),
|
||||||
|
@ -220,13 +270,22 @@ final class Wall extends VKAPIRequestHandler
|
||||||
"post_type" => "post",
|
"post_type" => "post",
|
||||||
"text" => $attachment->getText(false),
|
"text" => $attachment->getText(false),
|
||||||
"attachments" => $repostAttachments,
|
"attachments" => $repostAttachments,
|
||||||
"post_source" => [
|
"post_source" => $post_source,
|
||||||
"type" => "vk"
|
|
||||||
],
|
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$post_source = [];
|
||||||
|
|
||||||
|
if($post->getPlatform(true) === NULL) {
|
||||||
|
$post_source = (object)["type" => "vk"];
|
||||||
|
} else {
|
||||||
|
$post_source = (object)[
|
||||||
|
"type" => "api",
|
||||||
|
"platform" => $post->getPlatform(true)
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
$items[] = (object)[
|
$items[] = (object)[
|
||||||
"id" => $post->getVirtualId(),
|
"id" => $post->getVirtualId(),
|
||||||
"from_id" => $from_id,
|
"from_id" => $from_id,
|
||||||
|
@ -241,7 +300,8 @@ final class Wall extends VKAPIRequestHandler
|
||||||
"can_archive" => false, # TODO MAYBE
|
"can_archive" => false, # TODO MAYBE
|
||||||
"is_archived" => false,
|
"is_archived" => false,
|
||||||
"is_pinned" => $post->isPinned(),
|
"is_pinned" => $post->isPinned(),
|
||||||
"post_source" => (object)["type" => "vk"],
|
"is_explicit" => $post->isExplicit(),
|
||||||
|
"post_source" => $post_source,
|
||||||
"attachments" => $attachments,
|
"attachments" => $attachments,
|
||||||
"comments" => (object)[
|
"comments" => (object)[
|
||||||
"count" => $post->getCommentsCount(),
|
"count" => $post->getCommentsCount(),
|
||||||
|
@ -258,7 +318,7 @@ final class Wall extends VKAPIRequestHandler
|
||||||
"user_reposted" => 0
|
"user_reposted" => 0
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
|
|
||||||
if ($from_id > 0)
|
if ($from_id > 0)
|
||||||
$profiles[] = $from_id;
|
$profiles[] = $from_id;
|
||||||
else
|
else
|
||||||
|
@ -288,7 +348,8 @@ final class Wall extends VKAPIRequestHandler
|
||||||
"screen_name" => $user->getShortCode(),
|
"screen_name" => $user->getShortCode(),
|
||||||
"photo_50" => $user->getAvatarUrl(),
|
"photo_50" => $user->getAvatarUrl(),
|
||||||
"photo_100" => $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_50" => $group->getAvatarUrl(),
|
||||||
"photo_100" => $group->getAvatarUrl(),
|
"photo_100" => $group->getAvatarUrl(),
|
||||||
"photo_200" => $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->requireUser();
|
||||||
|
$this->willExecuteWriteAction();
|
||||||
|
|
||||||
$owner_id = intval($owner_id);
|
$owner_id = intval($owner_id);
|
||||||
|
|
||||||
$wallOwner = ($owner_id > 0 ? (new UsersRepo)->get($owner_id) : (new ClubsRepo)->get($owner_id * -1))
|
$wallOwner = ($owner_id > 0 ? (new UsersRepo)->get($owner_id) : (new ClubsRepo)->get($owner_id * -1))
|
||||||
?? $this->fail(18, "User was deleted or banned");
|
?? $this->fail(18, "User was deleted or banned");
|
||||||
if($owner_id > 0)
|
if($owner_id > 0)
|
||||||
|
@ -333,7 +396,7 @@ final class Wall extends VKAPIRequestHandler
|
||||||
else
|
else
|
||||||
$canPost = $wallOwner->canPost();
|
$canPost = $wallOwner->canPost();
|
||||||
else
|
else
|
||||||
$canPost = false;
|
$canPost = false;
|
||||||
|
|
||||||
if($canPost == false) $this->fail(15, "Access denied");
|
if($canPost == false) $this->fail(15, "Access denied");
|
||||||
|
|
||||||
|
@ -354,27 +417,7 @@ final class Wall extends VKAPIRequestHandler
|
||||||
if($signed == 1)
|
if($signed == 1)
|
||||||
$flags |= 0b01000000;
|
$flags |= 0b01000000;
|
||||||
|
|
||||||
# TODO: Compatible implementation of this
|
if(empty($message) && empty($attachments))
|
||||||
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)
|
|
||||||
$this->fail(100, "Required parameter 'message' missing.");
|
$this->fail(100, "Required parameter 'message' missing.");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -384,16 +427,69 @@ final class Wall extends VKAPIRequestHandler
|
||||||
$post->setCreated(time());
|
$post->setCreated(time());
|
||||||
$post->setContent($message);
|
$post->setContent($message);
|
||||||
$post->setFlags($flags);
|
$post->setFlags($flags);
|
||||||
|
$post->setApi_Source_Name($this->getPlatform());
|
||||||
$post->save();
|
$post->save();
|
||||||
} catch(\LogicException $ex) {
|
} catch(\LogicException $ex) {
|
||||||
$this->fail(100, "One of the parameters specified was missing or invalid");
|
$this->fail(100, "One of the parameters specified was missing or invalid");
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!is_null($photo))
|
if(!empty($attachments)) {
|
||||||
$post->attach($photo);
|
$attachmentsArr = explode(",", $attachments);
|
||||||
|
# Аттачи такого вида: [тип][id владельца]_[id вложения]
|
||||||
|
# Пример: photo1_1
|
||||||
|
|
||||||
if(!is_null($video))
|
if(sizeof($attachmentsArr) > 10)
|
||||||
$post->attach($video);
|
$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, "Photo does not exists");
|
||||||
|
if($attacc->getOwner()->getId() != $this->getUser()->getId())
|
||||||
|
$this->fail(43, "You do not have access to this photo");
|
||||||
|
|
||||||
|
$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()->getId() != $this->getUser()->getId())
|
||||||
|
$this->fail(43, "You do not have access to this video");
|
||||||
|
|
||||||
|
$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()->getId() != $this->getUser()->getId())
|
||||||
|
$this->fail(43, "You do not have access to this note");
|
||||||
|
|
||||||
|
if($attacc->getOwner()->getPrivacySetting("notes.read") < 1)
|
||||||
|
$this->fail(11, "You can't attach note to post, because your notes list is closed. Change it in privacy settings in web-version.");
|
||||||
|
|
||||||
|
$post->attach($attacc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if($wall > 0 && $wall !== $this->user->identity->getId())
|
if($wall > 0 && $wall !== $this->user->identity->getId())
|
||||||
(new WallPostNotification($wallOwner, $post, $this->user->identity))->emit();
|
(new WallPostNotification($wallOwner, $post, $this->user->identity))->emit();
|
||||||
|
@ -401,8 +497,9 @@ final class Wall extends VKAPIRequestHandler
|
||||||
return (object)["post_id" => $post->getVirtualId()];
|
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->requireUser();
|
||||||
|
$this->willExecuteWriteAction();
|
||||||
|
|
||||||
$postArray;
|
$postArray;
|
||||||
if(preg_match('/wall((?:-?)[0-9]+)_([0-9]+)/', $object, $postArray) == 0)
|
if(preg_match('/wall((?:-?)[0-9]+)_([0-9]+)/', $object, $postArray) == 0)
|
||||||
|
@ -413,13 +510,27 @@ final class Wall extends VKAPIRequestHandler
|
||||||
|
|
||||||
$nPost = new Post;
|
$nPost = new Post;
|
||||||
$nPost->setOwner($this->user->getId());
|
$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->setContent($message);
|
||||||
|
$nPost->setApi_Source_Name($this->getPlatform());
|
||||||
$nPost->save();
|
$nPost->save();
|
||||||
$nPost->attach($post);
|
$nPost->attach($post);
|
||||||
|
|
||||||
if($post->getOwner(false)->getId() !== $this->user->getId() && !($post->getOwner() instanceof Club))
|
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) [
|
return (object) [
|
||||||
"success" => 1, // 👍
|
"success" => 1, // 👍
|
||||||
|
@ -429,6 +540,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) {
|
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();
|
$this->requireUser();
|
||||||
|
|
||||||
|
@ -436,7 +548,7 @@ final class Wall extends VKAPIRequestHandler
|
||||||
if(!$post || $post->isDeleted()) $this->fail(100, "One of the parameters specified was missing or invalid");
|
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");
|
$comments = (new CommentsRepo)->getCommentsByTarget($post, $offset+1, $count, $sort == "desc" ? "DESC" : "ASC");
|
||||||
|
|
||||||
$items = [];
|
$items = [];
|
||||||
$profiles = [];
|
$profiles = [];
|
||||||
|
|
||||||
|
@ -445,7 +557,17 @@ final class Wall extends VKAPIRequestHandler
|
||||||
$oid = $owner->getId();
|
$oid = $owner->getId();
|
||||||
if($owner instanceof Club)
|
if($owner instanceof Club)
|
||||||
$oid *= -1;
|
$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 = [
|
$item = [
|
||||||
"id" => $comment->getId(),
|
"id" => $comment->getId(),
|
||||||
"from_id" => $oid,
|
"from_id" => $oid,
|
||||||
|
@ -454,6 +576,7 @@ final class Wall extends VKAPIRequestHandler
|
||||||
"post_id" => $post->getVirtualId(),
|
"post_id" => $post->getVirtualId(),
|
||||||
"owner_id" => $post->isPostedOnBehalfOfGroup() ? $post->getOwner()->getId() * -1 : $post->getOwner()->getId(),
|
"owner_id" => $post->isPostedOnBehalfOfGroup() ? $post->getOwner()->getId() * -1 : $post->getOwner()->getId(),
|
||||||
"parents_stack" => [],
|
"parents_stack" => [],
|
||||||
|
"attachments" => $attachments,
|
||||||
"thread" => [
|
"thread" => [
|
||||||
"count" => 0,
|
"count" => 0,
|
||||||
"items" => [],
|
"items" => [],
|
||||||
|
@ -470,10 +593,13 @@ final class Wall extends VKAPIRequestHandler
|
||||||
"user_likes" => (int) $comment->hasLikeFrom($this->getUser()),
|
"user_likes" => (int) $comment->hasLikeFrom($this->getUser()),
|
||||||
"can_publish" => 1
|
"can_publish" => 1
|
||||||
];
|
];
|
||||||
|
|
||||||
$items[] = $item;
|
$items[] = $item;
|
||||||
if($extended == true)
|
if($extended == true)
|
||||||
$profiles[] = $comment->getOwner()->getId();
|
$profiles[] = $comment->getOwner()->getId();
|
||||||
|
|
||||||
|
$attachments = null;
|
||||||
|
// Reset $attachments to not duplicate prikols
|
||||||
}
|
}
|
||||||
|
|
||||||
$response = [
|
$response = [
|
||||||
|
@ -496,10 +622,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") {
|
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();
|
$this->requireUser();
|
||||||
|
|
||||||
$comment = (new CommentsRepo)->get($comment_id); // один хуй айди всех комментов общий
|
$comment = (new CommentsRepo)->get($comment_id); # один хуй айди всех комментов общий
|
||||||
|
|
||||||
$profiles = [];
|
$profiles = [];
|
||||||
|
|
||||||
|
$attachments = [];
|
||||||
|
|
||||||
|
foreach($comment->getChildren() as $attachment) {
|
||||||
|
if($attachment instanceof \openvk\Web\Models\Entities\Photo) {
|
||||||
|
$attachments[] = $this->getApiPhoto($attachment);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$item = [
|
$item = [
|
||||||
"id" => $comment->getId(),
|
"id" => $comment->getId(),
|
||||||
"from_id" => $comment->getOwner()->getId(),
|
"from_id" => $comment->getOwner()->getId(),
|
||||||
|
@ -508,6 +642,7 @@ final class Wall extends VKAPIRequestHandler
|
||||||
"post_id" => $comment->getTarget()->getVirtualId(),
|
"post_id" => $comment->getTarget()->getVirtualId(),
|
||||||
"owner_id" => $comment->getTarget()->isPostedOnBehalfOfGroup() ? $comment->getTarget()->getOwner()->getId() * -1 : $comment->getTarget()->getOwner()->getId(),
|
"owner_id" => $comment->getTarget()->isPostedOnBehalfOfGroup() ? $comment->getTarget()->getOwner()->getId() * -1 : $comment->getTarget()->getOwner()->getId(),
|
||||||
"parents_stack" => [],
|
"parents_stack" => [],
|
||||||
|
"attachments" => $attachments,
|
||||||
"likes" => [
|
"likes" => [
|
||||||
"can_like" => 1,
|
"can_like" => 1,
|
||||||
"count" => $comment->getLikesCount(),
|
"count" => $comment->getLikesCount(),
|
||||||
|
@ -522,7 +657,7 @@ final class Wall extends VKAPIRequestHandler
|
||||||
"groups_can_post" => false,
|
"groups_can_post" => false,
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
|
|
||||||
if($extended == true)
|
if($extended == true)
|
||||||
$profiles[] = $comment->getOwner()->getId();
|
$profiles[] = $comment->getOwner()->getId();
|
||||||
|
|
||||||
|
@ -538,20 +673,29 @@ final class Wall extends VKAPIRequestHandler
|
||||||
$response['profiles'] = (!empty($profiles) ? (new Users)->get(implode(',', $profiles), $fields) : []);
|
$response['profiles'] = (!empty($profiles) ? (new Users)->get(implode(',', $profiles), $fields) : []);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return $response;
|
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);
|
$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)
|
if($post->getTargetWall() < 0)
|
||||||
$club = (new ClubsRepo)->get(abs($post->getTargetWall()));
|
$club = (new ClubsRepo)->get(abs($post->getTargetWall()));
|
||||||
|
|
||||||
|
if(empty($message) && empty($attachments)) {
|
||||||
|
$this->fail(100, "Required parameter 'message' missing.");
|
||||||
|
}
|
||||||
|
|
||||||
$flags = 0;
|
$flags = 0;
|
||||||
if($from_group != 0 && !is_null($club) && $club->canBeModifiedBy($this->user))
|
if($from_group != 0 && !is_null($club) && $club->canBeModifiedBy($this->user))
|
||||||
$flags |= 0b10000000;
|
$flags |= 0b10000000;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$comment = new Comment;
|
$comment = new Comment;
|
||||||
$comment->setOwner($this->user->getId());
|
$comment->setOwner($this->user->getId());
|
||||||
|
@ -564,11 +708,54 @@ final class Wall extends VKAPIRequestHandler
|
||||||
} catch (\LengthException $ex) {
|
} catch (\LengthException $ex) {
|
||||||
$this->fail(1, "ошибка про то что коммент большой слишком");
|
$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()->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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if($post->getOwner()->getId() !== $this->user->getId())
|
if($post->getOwner()->getId() !== $this->user->getId())
|
||||||
if(($owner = $post->getOwner()) instanceof User)
|
if(($owner = $post->getOwner()) instanceof User)
|
||||||
(new CommentNotification($owner, $comment, $post, $this->user))->emit();
|
(new CommentNotification($owner, $comment, $post, $this->user))->emit();
|
||||||
|
|
||||||
return (object) [
|
return (object) [
|
||||||
"comment_id" => $comment->getId(),
|
"comment_id" => $comment->getId(),
|
||||||
"parents_stack" => []
|
"parents_stack" => []
|
||||||
|
@ -577,17 +764,18 @@ final class Wall extends VKAPIRequestHandler
|
||||||
|
|
||||||
function deleteComment(int $comment_id) {
|
function deleteComment(int $comment_id) {
|
||||||
$this->requireUser();
|
$this->requireUser();
|
||||||
|
$this->willExecuteWriteAction();
|
||||||
|
|
||||||
$comment = (new CommentsRepo)->get($comment_id);
|
$comment = (new CommentsRepo)->get($comment_id);
|
||||||
if(!$comment) $this->fail(100, "One of the parameters specified was missing or invalid");;
|
if(!$comment) $this->fail(100, "One of the parameters specified was missing or invalid");;
|
||||||
if(!$comment->canBeDeletedBy($this->user))
|
if(!$comment->canBeDeletedBy($this->user))
|
||||||
$this->fail(7, "Access denied");
|
$this->fail(7, "Access denied");
|
||||||
|
|
||||||
$comment->delete();
|
$comment->delete();
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function getApiPhoto($attachment) {
|
private function getApiPhoto($attachment) {
|
||||||
return [
|
return [
|
||||||
"type" => "photo",
|
"type" => "photo",
|
||||||
|
@ -596,7 +784,7 @@ final class Wall extends VKAPIRequestHandler
|
||||||
"date" => $attachment->getPublicationTime()->timestamp(),
|
"date" => $attachment->getPublicationTime()->timestamp(),
|
||||||
"id" => $attachment->getVirtualId(),
|
"id" => $attachment->getVirtualId(),
|
||||||
"owner_id" => $attachment->getOwner()->getId(),
|
"owner_id" => $attachment->getOwner()->getId(),
|
||||||
"sizes" => array_values($attachment->getVkApiSizes()),
|
"sizes" => !is_null($attachment->getVkApiSizes()) ? array_values($attachment->getVkApiSizes()) : NULL,
|
||||||
"text" => "",
|
"text" => "",
|
||||||
"has_tags" => false
|
"has_tags" => false
|
||||||
]
|
]
|
||||||
|
@ -613,7 +801,7 @@ final class Wall extends VKAPIRequestHandler
|
||||||
"votes" => $answer->votes
|
"votes" => $answer->votes
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
$userVote = array();
|
$userVote = array();
|
||||||
foreach($attachment->getUserVote($user) as $vote)
|
foreach($attachment->getUserVote($user) as $vote)
|
||||||
$userVote[] = $vote[0];
|
$userVote[] = $vote[0];
|
||||||
|
|
|
@ -5,7 +5,7 @@ exceptions. It is still a work-in-progress functionality.
|
||||||
**Note**: requests to API are routed through
|
**Note**: requests to API are routed through
|
||||||
openvk.Web.Presenters.VKAPIPresenter, this dir contains only handlers.
|
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
|
## Implementing API methods
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,11 @@ class APIToken extends RowModel
|
||||||
{
|
{
|
||||||
return $this->getId() . "-" . chunk_split($this->getSecret(), 8, "-") . "jill";
|
return $this->getId() . "-" . chunk_split($this->getSecret(), 8, "-") . "jill";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getPlatform(): ?string
|
||||||
|
{
|
||||||
|
return $this->getRecord()->platform;
|
||||||
|
}
|
||||||
|
|
||||||
function isRevoked(): bool
|
function isRevoked(): bool
|
||||||
{
|
{
|
||||||
|
|
|
@ -66,4 +66,31 @@ class Album extends MediaCollection
|
||||||
{
|
{
|
||||||
return $this->has($photo);
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -160,7 +160,7 @@ class Club extends RowModel
|
||||||
|
|
||||||
function canPost(): bool
|
function canPost(): bool
|
||||||
{
|
{
|
||||||
return (bool) $this->getRecord()->wall;
|
return (bool) $this->getRecord()->wall;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -262,12 +262,12 @@ class Club extends RowModel
|
||||||
return $subbed && ($this->getOpennesStatus() === static::CLOSED ? $this->isSubscriptionAccepted($user) : true);
|
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");
|
$query = $this->getRecord()->related("subscriptions.target");
|
||||||
|
|
||||||
if($this->getOpennesStatus() === static::OPEN) {
|
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 {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -280,9 +280,9 @@ class Club extends RowModel
|
||||||
return sizeof($this->getFollowersQuery());
|
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) {
|
foreach($rels as $rel) {
|
||||||
$rel = (new Users)->get($rel->follower);
|
$rel = (new Users)->get($rel->follower);
|
||||||
|
@ -360,5 +360,35 @@ class Club extends RowModel
|
||||||
return $this->getRecord()->alert;
|
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;
|
use Traits\TSubscribable;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
namespace openvk\Web\Models\Entities;
|
namespace openvk\Web\Models\Entities;
|
||||||
use openvk\Web\Models\Repositories\Clubs;
|
use openvk\Web\Models\Repositories\Clubs;
|
||||||
use openvk\Web\Models\RowModel;
|
use openvk\Web\Models\RowModel;
|
||||||
|
use openvk\Web\Models\Entities\{Note};
|
||||||
|
|
||||||
class Comment extends Post
|
class Comment extends Post
|
||||||
{
|
{
|
||||||
|
@ -52,4 +53,36 @@ class Comment extends Post
|
||||||
$this->getTarget() instanceof Post && $this->getTarget()->getTargetWall() < 0 && (new Clubs)->get(abs($this->getTarget()->getTargetWall()))->canBeModifiedBy($user) ||
|
$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);
|
$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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -118,4 +118,24 @@ class Note extends Postable
|
||||||
{
|
{
|
||||||
return $this->getRecord()->source;
|
return $this->getRecord()->source;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function toVkApiStruct(): object
|
||||||
|
{
|
||||||
|
$res = (object) [];
|
||||||
|
|
||||||
|
$res->type = "note";
|
||||||
|
$res->id = $this->getId();
|
||||||
|
$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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,6 @@ final class CommentNotification extends Notification
|
||||||
|
|
||||||
function __construct(User $recipient, Comment $comment, $postable, User $commenter)
|
function __construct(User $recipient, Comment $comment, $postable, User $commenter)
|
||||||
{
|
{
|
||||||
parent::__construct($recipient, $postable, $commenter, time(), ovk_proc_strtr($comment->getText(), 10));
|
parent::__construct($recipient, $postable, $commenter, time(), ovk_proc_strtr(strip_tags($comment->getText()), 400));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,14 @@
|
||||||
<?php declare(strict_types=1);
|
<?php declare(strict_types=1);
|
||||||
namespace openvk\Web\Models\Entities\Notifications;
|
namespace openvk\Web\Models\Entities\Notifications;
|
||||||
|
use openvk\Web\Models\Entities\Postable;
|
||||||
use openvk\Web\Models\Entities\User;
|
use openvk\Web\Models\Entities\User;
|
||||||
|
|
||||||
final class MentionNotification extends Notification
|
final class MentionNotification extends Notification
|
||||||
{
|
{
|
||||||
protected $actionCode = 4;
|
protected $actionCode = 4;
|
||||||
|
|
||||||
function __construct(User $recipient, User $target, User $mentioner)
|
function __construct(User $recipient, Postable $discussionHost, $mentioner, string $quote = "")
|
||||||
{
|
{
|
||||||
parent::__construct($recipient, $target, $mentioner, time(), "");
|
parent::__construct($recipient, $mentioner, $discussionHost, time(), $quote);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,6 +30,11 @@ class Notification
|
||||||
return (int) json_decode(file_get_contents(__DIR__ . "/../../../../data/modelCodes.json"), true)[get_class($model)];
|
return (int) json_decode(file_get_contents(__DIR__ . "/../../../../data/modelCodes.json"), true)[get_class($model)];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function reverseModelOrder(): bool
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
function getActionCode(): int
|
function getActionCode(): int
|
||||||
{
|
{
|
||||||
return $this->actionCode;
|
return $this->actionCode;
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
<?php declare(strict_types=1);
|
<?php declare(strict_types=1);
|
||||||
namespace openvk\Web\Models\Entities;
|
namespace openvk\Web\Models\Entities;
|
||||||
use MessagePack\MessagePack;
|
use MessagePack\MessagePack;
|
||||||
|
use Nette\Utils\ImageException;
|
||||||
|
use Nette\Utils\UnknownImageFileException;
|
||||||
use openvk\Web\Models\Entities\Album;
|
use openvk\Web\Models\Entities\Album;
|
||||||
use openvk\Web\Models\Repositories\Albums;
|
use openvk\Web\Models\Repositories\Albums;
|
||||||
use Chandler\Database\DatabaseConnection as DB;
|
use Chandler\Database\DatabaseConnection as DB;
|
||||||
|
@ -13,48 +15,62 @@ class Photo extends Media
|
||||||
protected $fileExtension = "jpeg";
|
protected $fileExtension = "jpeg";
|
||||||
|
|
||||||
const ALLOWED_SIDE_MULTIPLIER = 7;
|
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];
|
$res = [false];
|
||||||
$image = Image::fromFile($filename);
|
|
||||||
$requiresProportion = ((string) $size["requireProp"]) != "none";
|
$requiresProportion = ((string) $size["requireProp"]) != "none";
|
||||||
if($requiresProportion) {
|
if($requiresProportion) {
|
||||||
$props = explode(":", (string) $size["requireProp"]);
|
$props = explode(":", (string) $size["requireProp"]);
|
||||||
$px = (int) $props[0];
|
$px = (int) $props[0];
|
||||||
$py = (int) $props[1];
|
$py = (int) $props[1];
|
||||||
if(($image->getWidth() / $image->getHeight()) > ($px / $py)) {
|
if(($image->getImageWidth() / $image->getImageHeight()) > ($px / $py)) {
|
||||||
# For some weird reason using resize with EXACT flag causes system to consume an unholy amount of RAM
|
$height = (int) ceil(($px * $image->getImageWidth()) / $py);
|
||||||
$image->crop(0, 0, "100%", (int) ceil(($px * $image->getWidth()) / $py));
|
$image->cropImage($image->getImageWidth(), $height, 0, 0);
|
||||||
$res[0] = true;
|
$res[0] = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(isset($size["maxSize"])) {
|
if(isset($size["maxSize"])) {
|
||||||
$maxSize = (int) $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"])) {
|
} else if(isset($size["maxResolution"])) {
|
||||||
$resolution = explode("x", (string) $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 {
|
} else {
|
||||||
throw new \RuntimeException("Malformed size description: " . (string) $size["id"]);
|
throw new \RuntimeException("Malformed size description: " . (string) $size["id"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
$res[1] = $image->getWidth();
|
$res[1] = $image->getImageWidth();
|
||||||
$res[2] = $image->getHeight();
|
$res[2] = $image->getImageHeight();
|
||||||
if($res[1] <= 300 || $res[2] <= 300)
|
if($res[1] <= 300 || $res[2] <= 300)
|
||||||
$image->save("$outputDir/" . (string) $size["id"] . ".gif");
|
$image->writeImage("$outputDir/$size[id].gif");
|
||||||
else
|
else
|
||||||
$image->save("$outputDir/" . (string) $size["id"] . ".jpeg");
|
$image->writeImage("$outputDir/$size[id].jpeg");
|
||||||
|
|
||||||
imagedestroy($image->getImageResource());
|
$res[3] = true;
|
||||||
|
$image->destroy();
|
||||||
unset($image);
|
unset($image);
|
||||||
|
|
||||||
return $res;
|
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 = dirname($this->pathFromHash($hash));
|
||||||
$dir = "$dir/$hash" . "_cropped";
|
$dir = "$dir/$hash" . "_cropped";
|
||||||
if(!is_dir($dir)) {
|
if(!is_dir($dir)) {
|
||||||
|
@ -67,8 +83,13 @@ class Photo extends Media
|
||||||
throw new \RuntimeException("Could not load photosizes.xml!");
|
throw new \RuntimeException("Could not load photosizes.xml!");
|
||||||
|
|
||||||
$sizesMeta = [];
|
$sizesMeta = [];
|
||||||
foreach($sizes->Size as $size)
|
if(OPENVK_ROOT_CONF["openvk"]["preferences"]["photos"]["photoSaving"] === "quick") {
|
||||||
$sizesMeta[(string) $size["id"]] = $this->resizeImage($filename, $dir, $size);
|
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);
|
$sizesMeta = MessagePack::pack($sizesMeta);
|
||||||
$this->stateChanges("sizes", $sizesMeta);
|
$this->stateChanges("sizes", $sizesMeta);
|
||||||
|
@ -76,13 +97,19 @@ class Photo extends Media
|
||||||
|
|
||||||
protected function saveFile(string $filename, string $hash): bool
|
protected function saveFile(string $filename, string $hash): bool
|
||||||
{
|
{
|
||||||
$image = Image::fromFile($filename);
|
$image = new \Imagick;
|
||||||
if(($image->height >= ($image->width * Photo::ALLOWED_SIDE_MULTIPLIER)) || ($image->width >= ($image->height * Photo::ALLOWED_SIDE_MULTIPLIER)))
|
$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");
|
throw new ISE("Invalid layout: image is too wide/short");
|
||||||
|
|
||||||
$image->resize(8192, 4320, Image::SHRINK_ONLY | Image::FIT);
|
$sizes = Image::calculateSize(
|
||||||
$image->save($this->pathFromHash($hash), 92, Image::JPEG);
|
$image->getImageWidth(), $image->getImageHeight(), 8192, 4320, Image::SHRINK_ONLY | Image::FIT
|
||||||
$this->saveImageResizedCopies($filename, $hash);
|
);
|
||||||
|
$image->resizeImage($sizes[0], $sizes[1], \Imagick::FILTER_HERMITE, 1);
|
||||||
|
$image->writeImage($this->pathFromHash($hash));
|
||||||
|
$this->saveImageResizedCopies($image, $filename, $hash);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -114,8 +141,8 @@ class Photo extends Media
|
||||||
$sizes = $this->getRecord()->sizes;
|
$sizes = $this->getRecord()->sizes;
|
||||||
if(!$sizes || $forceUpdate) {
|
if(!$sizes || $forceUpdate) {
|
||||||
if($forceUpdate || $upgrade || OPENVK_ROOT_CONF["openvk"]["preferences"]["photos"]["upgradeStructure"]) {
|
if($forceUpdate || $upgrade || OPENVK_ROOT_CONF["openvk"]["preferences"]["photos"]["upgradeStructure"]) {
|
||||||
$hash = $this->getRecord()->hash;
|
$hash = $this->getRecord()->hash;
|
||||||
$this->saveImageResizedCopies($this->pathFromHash($hash), $hash);
|
$this->saveImageResizedCopies(NULL, $this->pathFromHash($hash), $hash);
|
||||||
$this->save();
|
$this->save();
|
||||||
|
|
||||||
return $this->getSizes();
|
return $this->getSizes();
|
||||||
|
@ -127,6 +154,16 @@ class Photo extends Media
|
||||||
$res = [];
|
$res = [];
|
||||||
$sizes = MessagePack::unpack($sizes);
|
$sizes = MessagePack::unpack($sizes);
|
||||||
foreach($sizes as $id => $meta) {
|
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 = $this->getURL();
|
||||||
$url = str_replace(".$this->fileExtension", "_cropped/$id.", $url);
|
$url = str_replace(".$this->fileExtension", "_cropped/$id.", $url);
|
||||||
$url .= ($meta[1] <= 300 || $meta[2] <= 300) ? "gif" : "jpeg";
|
$url .= ($meta[1] <= 300 || $meta[2] <= 300) ? "gif" : "jpeg";
|
||||||
|
@ -149,6 +186,47 @@ class Photo extends Media
|
||||||
|
|
||||||
return $res;
|
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
|
function getVkApiSizes(): ?array
|
||||||
{
|
{
|
||||||
|
@ -205,30 +283,48 @@ class Photo extends Media
|
||||||
return [$x, $y];
|
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
|
function getAlbum(): ?Album
|
||||||
{
|
{
|
||||||
return (new Albums)->getAlbumByPhotoId($this);
|
return (new Albums)->getAlbumByPhotoId($this);
|
||||||
}
|
}
|
||||||
|
|
||||||
function toVkApiStruct(): object
|
function toVkApiStruct(bool $photo_sizes = true, bool $extended = false): object
|
||||||
{
|
{
|
||||||
$res = (object) [];
|
$res = (object) [];
|
||||||
|
|
||||||
$res->id = $res->pid = $this->getId();
|
$res->id = $res->pid = $this->getVirtualId();
|
||||||
$res->owner_id = $res->user_id = $this->getOwner()->getId()->getId();
|
$res->owner_id = $res->user_id = $this->getOwner()->getId();
|
||||||
$res->aid = $res->album_id = NULL;
|
$res->aid = $res->album_id = NULL;
|
||||||
$res->width = $this->getDimensions()[0];
|
$res->width = $this->getDimensions()[0];
|
||||||
$res->height = $this->getDimensions()[1];
|
$res->height = $this->getDimensions()[1];
|
||||||
$res->date = $res->created = $this->getPublicationTime()->timestamp();
|
$res->date = $res->created = $this->getPublicationTime()->timestamp();
|
||||||
|
|
||||||
$res->sizes = $this->getVkApiSizes();
|
if($photo_sizes) {
|
||||||
$res->src_small = $res->photo_75 = $this->getURLBySizeId("miniscule");
|
$res->sizes = $this->getVkApiSizes();
|
||||||
$res->src = $res->photo_130 = $this->getURLBySizeId("tiny");
|
$res->src_small = $res->photo_75 = $this->getURLBySizeId("miniscule");
|
||||||
$res->src_big = $res->photo_604 = $this->getURLBySizeId("normal");
|
$res->src = $res->photo_130 = $this->getURLBySizeId("tiny");
|
||||||
$res->src_xbig = $res->photo_807 = $this->getURLBySizeId("large");
|
$res->src_big = $res->photo_604 = $this->getURLBySizeId("normal");
|
||||||
$res->src_xxbig = $res->photo_1280 = $this->getURLBySizeId("larger");
|
$res->src_xbig = $res->photo_807 = $this->getURLBySizeId("large");
|
||||||
$res->src_xxxbig = $res->photo_2560 = $this->getURLBySizeId("original");
|
$res->src_xxbig = $res->photo_1280 = $this->getURLBySizeId("larger");
|
||||||
$res->src_original = $res->url = $this->getURLBySizeId("UPLOADED_MAXRES");
|
$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;
|
return $res;
|
||||||
}
|
}
|
||||||
|
|
|
@ -99,6 +99,11 @@ class Post extends Postable
|
||||||
return (($this->getRecord()->flags & 0b00100000) > 0) && ($this->getRecord()->owner > 0);
|
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
|
function isExplicit(): bool
|
||||||
{
|
{
|
||||||
return (bool) $this->getRecord()->nsfw;
|
return (bool) $this->getRecord()->nsfw;
|
||||||
|
@ -113,6 +118,63 @@ class Post extends Postable
|
||||||
{
|
{
|
||||||
return $this->getOwner(false)->getId();
|
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
|
function pin(): void
|
||||||
{
|
{
|
||||||
|
|
|
@ -68,6 +68,12 @@ class Topic extends Postable
|
||||||
return isset($array[0]) ? $array[0] : NULL;
|
return isset($array[0]) ? $array[0] : NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getFirstComment(): ?Comment
|
||||||
|
{
|
||||||
|
$array = iterator_to_array($this->getComments(1));
|
||||||
|
return $array[0] ?? NULL;
|
||||||
|
}
|
||||||
|
|
||||||
function getUpdateTime(): DateTime
|
function getUpdateTime(): DateTime
|
||||||
{
|
{
|
||||||
$lastComment = $this->getLastComment();
|
$lastComment = $this->getLastComment();
|
||||||
|
@ -83,4 +89,40 @@ class Topic extends Postable
|
||||||
$this->unwire();
|
$this->unwire();
|
||||||
$this->save();
|
$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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
44
Web/Models/Entities/Traits/TBackDrops.php
Normal file
44
Web/Models/Entities/Traits/TBackDrops.php
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
<?php declare(strict_types=1);
|
||||||
|
namespace openvk\Web\Models\Entities\Traits;
|
||||||
|
|
||||||
|
use openvk\Web\Models\Entities\Photo;
|
||||||
|
use openvk\Web\Models\Repositories\Photos;
|
||||||
|
|
||||||
|
trait TBackDrops {
|
||||||
|
function getBackDropPictureURLs(): ?array
|
||||||
|
{
|
||||||
|
$photo1 = $this->getRecord()->backdrop_1;
|
||||||
|
$photo2 = $this->getRecord()->backdrop_2;
|
||||||
|
if(is_null($photo1) && is_null($photo2))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
$photo1obj = $photo2obj = NULL;
|
||||||
|
if(!is_null($photo1))
|
||||||
|
$photo1obj = (new Photos)->get($photo1);
|
||||||
|
if(!is_null($photo2))
|
||||||
|
$photo2obj = (new Photos)->get($photo2);
|
||||||
|
|
||||||
|
if(is_null($photo1obj) && is_null($photo2obj))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return [
|
||||||
|
is_null($photo1obj) ? "" : $photo1obj->getURL(),
|
||||||
|
is_null($photo2obj) ? "" : $photo2obj->getURL(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
function setBackDropPictures(?Photo $first, ?Photo $second): void
|
||||||
|
{
|
||||||
|
if(!is_null($first))
|
||||||
|
$this->stateChanges("backdrop_1", $first->getId());
|
||||||
|
|
||||||
|
if(!is_null($second))
|
||||||
|
$this->stateChanges("backdrop_2", $second->getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
function unsetBackDropPictures(): void
|
||||||
|
{
|
||||||
|
$this->stateChanges("backdrop_1", NULL);
|
||||||
|
$this->stateChanges("backdrop_2", NULL);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
<?php declare(strict_types=1);
|
<?php declare(strict_types=1);
|
||||||
namespace openvk\Web\Models\Entities\Traits;
|
namespace openvk\Web\Models\Entities\Traits;
|
||||||
|
use openvk\Web\Models\Repositories\{Users, Clubs};
|
||||||
use Wkhooy\ObsceneCensorRus;
|
use Wkhooy\ObsceneCensorRus;
|
||||||
|
|
||||||
trait TRichText
|
trait TRichText
|
||||||
|
@ -35,9 +36,9 @@ trait TRichText
|
||||||
"%(([A-z]++):\/\/(\S*?\.\S*?))([\s)\[\]{},\"\'<]|\.\s|$)%",
|
"%(([A-z]++):\/\/(\S*?\.\S*?))([\s)\[\]{},\"\'<]|\.\s|$)%",
|
||||||
(function (array $matches): string {
|
(function (array $matches): string {
|
||||||
$href = str_replace("#", "#", $matches[1]);
|
$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(";", ";", $matches[3]);
|
$link = str_replace(";", ";", $link);
|
||||||
$rel = $this->isAd() ? "sponsored" : "ugc";
|
$rel = $this->isAd() ? "sponsored" : "ugc";
|
||||||
|
|
||||||
return "<a href='/away.php?to=$href' rel='$rel' target='_blank'>$link</a>" . htmlentities($matches[4]);
|
return "<a href='/away.php?to=$href' rel='$rel' target='_blank'>$link</a>" . htmlentities($matches[4]);
|
||||||
|
@ -48,7 +49,63 @@ trait TRichText
|
||||||
|
|
||||||
private function removeZalgo(string $text): string
|
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
|
||||||
|
{
|
||||||
|
$contentColumn = property_exists($this, "overrideContentColumn") ? $this->overrideContentColumn : "content";
|
||||||
|
$text = $this->getRecord()->{$contentColumn};
|
||||||
|
$text = preg_replace("%@([A-Za-z0-9]++) \(((?:[\p{L&}\p{Lo} 0-9]\p{Mn}?)++)\)%Xu", "[$1|$2]", $text);
|
||||||
|
$text = preg_replace("%([\n\r\s]|^)(@([A-Za-z0-9]++))%Xu", "$1[$3|@$3]", $text);
|
||||||
|
|
||||||
|
$resolvedUsers = $skipUsers;
|
||||||
|
$resolvedClubs = [];
|
||||||
|
preg_match_all("%\[([A-Za-z0-9]++)\|((?:[\p{L&}\p{Lo} 0-9@]\p{Mn}?)++)\]%Xu", $text, $links, PREG_PATTERN_ORDER);
|
||||||
|
foreach($links[1] as $link) {
|
||||||
|
if(preg_match("%^id([0-9]++)$%", $link, $match)) {
|
||||||
|
$uid = (int) $match[1];
|
||||||
|
if(in_array($uid, $resolvedUsers))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
$resolvedUsers[] = $uid;
|
||||||
|
$maybeUser = (new Users)->get($uid);
|
||||||
|
if($maybeUser)
|
||||||
|
yield $maybeUser;
|
||||||
|
} else if(preg_match("%^(?:club|public|event)([0-9]++)$%", $link, $match)) {
|
||||||
|
$cid = (int) $match[1];
|
||||||
|
if(in_array($cid, $resolvedClubs))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
$resolvedClubs[] = $cid;
|
||||||
|
$maybeClub = (new Clubs)->get($cid);
|
||||||
|
if($maybeClub)
|
||||||
|
yield $maybeClub;
|
||||||
|
} else {
|
||||||
|
$maybeUser = (new Users)->getByShortURL($link);
|
||||||
|
if($maybeUser) {
|
||||||
|
$uid = $maybeUser->getId();
|
||||||
|
if(in_array($uid, $resolvedUsers))
|
||||||
|
continue;
|
||||||
|
else
|
||||||
|
$resolvedUsers[] = $uid;
|
||||||
|
|
||||||
|
yield $maybeUser;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$maybeClub = (new Clubs)->getByShortURL($link);
|
||||||
|
if($maybeClub) {
|
||||||
|
$cid = $maybeClub->getId();
|
||||||
|
if(in_array($cid, $resolvedClubs))
|
||||||
|
continue;
|
||||||
|
else
|
||||||
|
$resolvedClubs[] = $cid;
|
||||||
|
|
||||||
|
yield $maybeClub;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getText(bool $html = true): string
|
function getText(bool $html = true): string
|
||||||
|
@ -59,7 +116,6 @@ trait TRichText
|
||||||
$proc = iconv_strlen($this->getRecord()->{$contentColumn}) <= OPENVK_ROOT_CONF["openvk"]["preferences"]["wall"]["postSizes"]["processingLimit"];
|
$proc = iconv_strlen($this->getRecord()->{$contentColumn}) <= OPENVK_ROOT_CONF["openvk"]["preferences"]["wall"]["postSizes"]["processingLimit"];
|
||||||
if($html) {
|
if($html) {
|
||||||
if($proc) {
|
if($proc) {
|
||||||
$rel = $this->isAd() ? "sponsored" : "ugc";
|
|
||||||
$text = $this->formatLinks($text);
|
$text = $this->formatLinks($text);
|
||||||
$text = preg_replace("%@([A-Za-z0-9]++) \(((?:[\p{L&}\p{Lo} 0-9]\p{Mn}?)++)\)%Xu", "[$1|$2]", $text);
|
$text = preg_replace("%@([A-Za-z0-9]++) \(((?:[\p{L&}\p{Lo} 0-9]\p{Mn}?)++)\)%Xu", "[$1|$2]", $text);
|
||||||
$text = preg_replace("%([\n\r\s]|^)(@([A-Za-z0-9]++))%Xu", "$1[$3|@$3]", $text);
|
$text = preg_replace("%([\n\r\s]|^)(@([A-Za-z0-9]++))%Xu", "$1[$3|@$3]", $text);
|
||||||
|
|
|
@ -5,7 +5,7 @@ use openvk\Web\Themes\{Themepack, Themepacks};
|
||||||
use openvk\Web\Util\DateTime;
|
use openvk\Web\Util\DateTime;
|
||||||
use openvk\Web\Models\RowModel;
|
use openvk\Web\Models\RowModel;
|
||||||
use openvk\Web\Models\Entities\{Photo, Message, Correspondence, Gift};
|
use openvk\Web\Models\Entities\{Photo, Message, Correspondence, Gift};
|
||||||
use openvk\Web\Models\Repositories\{Users, Clubs, Albums, Gifts, Notifications};
|
use openvk\Web\Models\Repositories\{Photos, Users, Clubs, Albums, Gifts, Notifications};
|
||||||
use openvk\Web\Models\Exceptions\InvalidUserNameException;
|
use openvk\Web\Models\Exceptions\InvalidUserNameException;
|
||||||
use Nette\Database\Table\ActiveRow;
|
use Nette\Database\Table\ActiveRow;
|
||||||
use Chandler\Database\DatabaseConnection;
|
use Chandler\Database\DatabaseConnection;
|
||||||
|
@ -751,6 +751,63 @@ class User extends RowModel
|
||||||
return time() - $this->getRecord()->online <= 300;
|
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
|
function prefersNotToSeeRating(): bool
|
||||||
{
|
{
|
||||||
return !((bool) $this->getRecord()->show_rating);
|
return !((bool) $this->getRecord()->show_rating);
|
||||||
|
@ -952,6 +1009,15 @@ class User extends RowModel
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function updOnline(string $platform): bool
|
||||||
|
{
|
||||||
|
$this->setOnline(time());
|
||||||
|
$this->setClient_name($platform);
|
||||||
|
$this->save();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
function changeEmail(string $email): void
|
function changeEmail(string $email): void
|
||||||
{
|
{
|
||||||
DatabaseConnection::i()->getContext()->table("ChandlerUsers")
|
DatabaseConnection::i()->getContext()->table("ChandlerUsers")
|
||||||
|
@ -1043,6 +1109,24 @@ class User extends RowModel
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
use Traits\TSubscribable;
|
use Traits\TSubscribable;
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,7 @@ class Video extends Media
|
||||||
const TYPE_EMBED = 1;
|
const TYPE_EMBED = 1;
|
||||||
|
|
||||||
protected $tableName = "videos";
|
protected $tableName = "videos";
|
||||||
protected $fileExtension = "ogv";
|
protected $fileExtension = "mp4";
|
||||||
|
|
||||||
protected $processingPlaceholder = "video/rendering";
|
protected $processingPlaceholder = "video/rendering";
|
||||||
|
|
||||||
|
@ -30,7 +30,7 @@ class Video extends Media
|
||||||
throw new \DomainException("$filename does not contain any video streams");
|
throw new \DomainException("$filename does not contain any video streams");
|
||||||
|
|
||||||
$durations = [];
|
$durations = [];
|
||||||
preg_match('%duration=([0-9\.]++)%', $streams, $durations);
|
preg_match_all('%duration=([0-9\.]++)%', $streams, $durations);
|
||||||
if(sizeof($durations[1]) === 0)
|
if(sizeof($durations[1]) === 0)
|
||||||
throw new \DomainException("$filename does not contain any meaningful video streams");
|
throw new \DomainException("$filename does not contain any meaningful video streams");
|
||||||
|
|
||||||
|
@ -104,7 +104,7 @@ class Video extends Media
|
||||||
if(!$this->isProcessed())
|
if(!$this->isProcessed())
|
||||||
return "/assets/packages/static/openvk/video/rendering.apng";
|
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 {
|
} else {
|
||||||
return $this->getVideoDriver()->getThumbnailURL();
|
return $this->getVideoDriver()->getThumbnailURL();
|
||||||
}
|
}
|
||||||
|
@ -114,7 +114,64 @@ class Video extends Media
|
||||||
{
|
{
|
||||||
return $this->getRecord()->owner;
|
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
|
function setLink(string $link): string
|
||||||
{
|
{
|
||||||
if(preg_match(file_get_contents(__DIR__ . "/../VideoDrivers/regex/youtube.txt"), $link, $matches)) {
|
if(preg_match(file_get_contents(__DIR__ . "/../VideoDrivers/regex/youtube.txt"), $link, $matches)) {
|
||||||
|
@ -145,11 +202,14 @@ class Video extends Media
|
||||||
$this->save();
|
$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 = new Video;
|
||||||
$video->setOwner($owner);
|
$video->setOwner($owner);
|
||||||
$video->setName("Unnamed Video.ogv");
|
$video->setName(ovk_proc_strtr($name, 61));
|
||||||
$video->setDescription(ovk_proc_strtr($description, 300));
|
$video->setDescription(ovk_proc_strtr($description, 300));
|
||||||
$video->setAnonymous($anon);
|
$video->setAnonymous($anon);
|
||||||
$video->setCreated(time());
|
$video->setCreated(time());
|
||||||
|
|
|
@ -123,4 +123,14 @@ class Albums
|
||||||
|
|
||||||
return $dbalbum->collection ? $this->get($dbalbum->collection) : null;
|
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()));
|
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"));
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -1,7 +1,7 @@
|
||||||
<?php declare(strict_types=1);
|
<?php declare(strict_types=1);
|
||||||
namespace openvk\Web\Models\Repositories;
|
namespace openvk\Web\Models\Repositories;
|
||||||
use openvk\Web\Models\Entities\Club;
|
use openvk\Web\Models\Entities\{Club, Manager};
|
||||||
use openvk\Web\Models\Repositories\Aliases;
|
use openvk\Web\Models\Repositories\{Aliases, Users};
|
||||||
use Nette\Database\Table\ActiveRow;
|
use Nette\Database\Table\ActiveRow;
|
||||||
use Chandler\Database\DatabaseConnection;
|
use Chandler\Database\DatabaseConnection;
|
||||||
|
|
||||||
|
@ -9,11 +9,13 @@ class Clubs
|
||||||
{
|
{
|
||||||
private $context;
|
private $context;
|
||||||
private $clubs;
|
private $clubs;
|
||||||
|
private $coadmins;
|
||||||
|
|
||||||
function __construct()
|
function __construct()
|
||||||
{
|
{
|
||||||
$this->context = DatabaseConnection::i()->getContext();
|
$this->context = DatabaseConnection::i()->getContext();
|
||||||
$this->clubs = $this->context->table("groups");
|
$this->clubs = $this->context->table("groups");
|
||||||
|
$this->coadmins = $this->context->table("group_coadmins");
|
||||||
}
|
}
|
||||||
|
|
||||||
private function toClub(?ActiveRow $ar): ?Club
|
private function toClub(?ActiveRow $ar): ?Club
|
||||||
|
@ -41,12 +43,12 @@ class Clubs
|
||||||
return $this->toClub($this->clubs->get($id));
|
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%";
|
$query = "%$query%";
|
||||||
$result = $this->clubs->where("name LIKE ? OR about LIKE ?", $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
|
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;
|
use \Nette\SmartObject;
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,4 +59,35 @@ class Comments
|
||||||
"deleted" => false,
|
"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"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,10 +26,10 @@ class Notes
|
||||||
return $this->toNote($this->notes->get($id));
|
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;
|
$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);
|
yield new Note($album);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<?php declare(strict_types=1);
|
<?php declare(strict_types=1);
|
||||||
namespace openvk\Web\Models\Repositories;
|
namespace openvk\Web\Models\Repositories;
|
||||||
use openvk\Web\Models\Entities\Photo;
|
use openvk\Web\Models\Entities\{Photo, User};
|
||||||
use Chandler\Database\DatabaseConnection;
|
use Chandler\Database\DatabaseConnection;
|
||||||
|
|
||||||
class Photos
|
class Photos
|
||||||
|
@ -32,4 +32,15 @@ class Photos
|
||||||
|
|
||||||
return new Photo($photo);
|
return new Photo($photo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getEveryUserPhoto(User $user): \Traversable
|
||||||
|
{
|
||||||
|
$photos = $this->photos->where([
|
||||||
|
"owner" => $user->getId()
|
||||||
|
]);
|
||||||
|
|
||||||
|
foreach($photos as $photo) {
|
||||||
|
yield new Photo($photo);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -99,7 +99,39 @@ class Posts
|
||||||
return NULL;
|
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
|
function getPostCountOnUserWall(int $user): int
|
||||||
{
|
{
|
||||||
return sizeof($this->posts->where(["wall" => $user, "deleted" => 0]));
|
return sizeof($this->posts->where(["wall" => $user, "deleted" => 0]));
|
||||||
|
|
|
@ -49,12 +49,91 @@ class Users
|
||||||
return $this->toUser($this->users->where("user", $user->getId())->fetch());
|
return $this->toUser($this->users->where("user", $user->getId())->fetch());
|
||||||
}
|
}
|
||||||
|
|
||||||
function find(string $query): Util\EntityStream
|
function find(string $query, array $pars = [], string $sort = "id DESC"): Util\EntityStream
|
||||||
{
|
{
|
||||||
$query = "%$query%";
|
$query = "%$query%";
|
||||||
$result = $this->users->where("CONCAT_WS(' ', first_name, last_name, pseudo, shortcode) LIKE ?", $query)->where("deleted", 0);
|
$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
|
function getStatistics(): object
|
||||||
|
|
|
@ -45,4 +45,36 @@ class Videos
|
||||||
{
|
{
|
||||||
return sizeof($this->videos->where("owner", $user->getId())->where(["deleted" => 0, "unlisted" => 0]));
|
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 getURL(): string;
|
||||||
|
|
||||||
abstract function getEmbed(): string;
|
abstract function getEmbed(string $w = "600", string $h = "340"): string;
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,13 +13,13 @@ final class YouTubeVideoDriver extends VideoDriver
|
||||||
return "https://youtu.be/$this->id";
|
return "https://youtu.be/$this->id";
|
||||||
}
|
}
|
||||||
|
|
||||||
function getEmbed(): string
|
function getEmbed(string $w = "600", string $h = "340"): string
|
||||||
{
|
{
|
||||||
return <<<CODE
|
return <<<CODE
|
||||||
<iframe
|
<iframe
|
||||||
width="600"
|
width="$w"
|
||||||
height="340"
|
height="$h"
|
||||||
src="https://www.youtube.com/embed/$this->id"
|
src="https://www.youtube-nocookie.com/embed/$this->id"
|
||||||
frameborder="0"
|
frameborder="0"
|
||||||
sandbox="allow-same-origin allow-scripts allow-popups"
|
sandbox="allow-same-origin allow-scripts allow-popups"
|
||||||
allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture"
|
allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture"
|
||||||
|
|
|
@ -13,8 +13,8 @@ Move-Item $file $temp
|
||||||
|
|
||||||
# video stub logic was implicitly deprecated, so we start processing at once
|
# 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 -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 $temp
|
||||||
Remove-Item $temp2
|
Remove-Item $temp2
|
||||||
|
|
|
@ -1,14 +1,12 @@
|
||||||
tmpfile="$RANDOM-$(date +%s%N)"
|
tmpfile="$RANDOM-$(date +%s%N)"
|
||||||
|
|
||||||
cp $2 "/tmp/vid_$tmpfile.bin"
|
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 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
|
rm -rf $3${4:0:2}/$4.mp4
|
||||||
mv "/tmp/ffmOi$tmpfile.ogv" $3${4:0:2}/$4.ogv
|
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"
|
rm -f "/tmp/vid_$tmpfile.bin"
|
||||||
|
|
|
@ -37,6 +37,9 @@ final class AboutPresenter extends OpenVKPresenter
|
||||||
|
|
||||||
function renderBB(): void
|
function renderBB(): void
|
||||||
{}
|
{}
|
||||||
|
|
||||||
|
function renderTour(): void
|
||||||
|
{}
|
||||||
|
|
||||||
function renderInvite(): void
|
function renderInvite(): void
|
||||||
{
|
{
|
||||||
|
@ -138,6 +141,6 @@ final class AboutPresenter extends OpenVKPresenter
|
||||||
|
|
||||||
function renderDev(): void
|
function renderDev(): void
|
||||||
{
|
{
|
||||||
$this->redirect("https://docs.openvk.su/");
|
$this->redirect("https://docs.openvk.uk/");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -86,6 +86,9 @@ final class AdminPresenter extends OpenVKPresenter
|
||||||
$query = "INSERT INTO `ChandlerACLRelations` (`user`, `group`) VALUES ('" . $user->getChandlerGUID() . "', '" . $this->postParam("add-to-group") . "')";
|
$query = "INSERT INTO `ChandlerACLRelations` (`user`, `group`) VALUES ('" . $user->getChandlerGUID() . "', '" . $this->postParam("add-to-group") . "')";
|
||||||
DatabaseConnection::i()->getConnection()->query($query);
|
DatabaseConnection::i()->getConnection()->query($query);
|
||||||
}
|
}
|
||||||
|
if($this->postParam("password")) {
|
||||||
|
$user->getChandlerUser()->updatePassword($this->postParam("password"));
|
||||||
|
}
|
||||||
|
|
||||||
$user->save();
|
$user->save();
|
||||||
|
|
||||||
|
|
|
@ -80,7 +80,11 @@ final class AuthPresenter extends OpenVKPresenter
|
||||||
|
|
||||||
if(!Validator::i()->emailValid($this->postParam("email")))
|
if(!Validator::i()->emailValid($this->postParam("email")))
|
||||||
$this->flashFail("err", tr("invalid_email_address"), tr("invalid_email_address_comment"));
|
$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())
|
if (strtotime($this->postParam("birthday")) > time())
|
||||||
$this->flashFail("err", tr("invalid_birth_date"), tr("invalid_birth_date_comment"));
|
$this->flashFail("err", tr("invalid_birth_date"), tr("invalid_birth_date_comment"));
|
||||||
|
|
||||||
|
@ -203,6 +207,9 @@ final class AuthPresenter extends OpenVKPresenter
|
||||||
|
|
||||||
function renderFinishRestoringPassword(): void
|
function renderFinishRestoringPassword(): void
|
||||||
{
|
{
|
||||||
|
if(OPENVK_ROOT_CONF['openvk']['preferences']['security']['disablePasswordRestoring'])
|
||||||
|
$this->notFound();
|
||||||
|
|
||||||
$request = $this->restores->getByToken(str_replace(" ", "+", $this->queryParam("key")));
|
$request = $this->restores->getByToken(str_replace(" ", "+", $this->queryParam("key")));
|
||||||
if(!$request || !$request->isStillValid()) {
|
if(!$request || !$request->isStillValid()) {
|
||||||
$this->flash("err", tr("token_manipulation_error"), tr("token_manipulation_error_comment"));
|
$this->flash("err", tr("token_manipulation_error"), tr("token_manipulation_error_comment"));
|
||||||
|
@ -237,6 +244,9 @@ final class AuthPresenter extends OpenVKPresenter
|
||||||
|
|
||||||
function renderRestore(): void
|
function renderRestore(): void
|
||||||
{
|
{
|
||||||
|
if(OPENVK_ROOT_CONF['openvk']['preferences']['security']['disablePasswordRestoring'])
|
||||||
|
$this->notFound();
|
||||||
|
|
||||||
if(!is_null($this->user))
|
if(!is_null($this->user))
|
||||||
$this->redirect($this->user->identity->getURL());
|
$this->redirect($this->user->identity->getURL());
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<?php declare(strict_types=1);
|
<?php declare(strict_types=1);
|
||||||
namespace openvk\Web\Presenters;
|
namespace openvk\Web\Presenters;
|
||||||
use openvk\Web\Models\Entities\{Comment, Photo, Video, User, Topic, Post};
|
use openvk\Web\Models\Entities\{Comment, Notifications\MentionNotification, Photo, Video, User, Topic, Post};
|
||||||
use openvk\Web\Models\Entities\Notifications\CommentNotification;
|
use openvk\Web\Models\Entities\Notifications\CommentNotification;
|
||||||
use openvk\Web\Models\Repositories\{Comments, Clubs};
|
use openvk\Web\Models\Repositories\{Comments, Clubs};
|
||||||
|
|
||||||
|
@ -47,6 +47,9 @@ final class CommentPresenter extends OpenVKPresenter
|
||||||
$club = (new Clubs)->get(abs($entity->getTargetWall()));
|
$club = (new Clubs)->get(abs($entity->getTargetWall()));
|
||||||
else if($entity instanceof Topic)
|
else if($entity instanceof Topic)
|
||||||
$club = $entity->getClub();
|
$club = $entity->getClub();
|
||||||
|
|
||||||
|
if($_FILES["_vid_attachment"] && OPENVK_ROOT_CONF['openvk']['preferences']['videos']['disableUploading'])
|
||||||
|
$this->flashFail("err", tr("error"), "Video uploads are disabled by the system administrator.");
|
||||||
|
|
||||||
$anon = OPENVK_ROOT_CONF["openvk"]["preferences"]["wall"]["anonymousPosting"]["enable"] && $this->postParam("anon") === "on";
|
$anon = OPENVK_ROOT_CONF["openvk"]["preferences"]["wall"]["anonymousPosting"]["enable"] && $this->postParam("anon") === "on";
|
||||||
|
|
||||||
|
@ -76,7 +79,7 @@ final class CommentPresenter extends OpenVKPresenter
|
||||||
}
|
}
|
||||||
|
|
||||||
if($_FILES["_vid_attachment"]["error"] === UPLOAD_ERR_OK) {
|
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) {
|
} catch(ISE $ex) {
|
||||||
$this->flashFail("err", "Не удалось опубликовать комментарий", "Файл медиаконтента повреждён или слишком велик.");
|
$this->flashFail("err", "Не удалось опубликовать комментарий", "Файл медиаконтента повреждён или слишком велик.");
|
||||||
|
@ -108,6 +111,15 @@ final class CommentPresenter extends OpenVKPresenter
|
||||||
if($entity->getOwner()->getId() !== $this->user->identity->getId())
|
if($entity->getOwner()->getId() !== $this->user->identity->getId())
|
||||||
if(($owner = $entity->getOwner()) instanceof User)
|
if(($owner = $entity->getOwner()) instanceof User)
|
||||||
(new CommentNotification($owner, $comment, $entity, $this->user->identity))->emit();
|
(new CommentNotification($owner, $comment, $entity, $this->user->identity))->emit();
|
||||||
|
|
||||||
|
$excludeMentions = [$this->user->identity->getId()];
|
||||||
|
if(($owner = $entity->getOwner()) instanceof User)
|
||||||
|
$excludeMentions[] = $owner->getId();
|
||||||
|
|
||||||
|
$mentions = iterator_to_array($comment->resolveMentions($excludeMentions));
|
||||||
|
foreach($mentions as $mentionee)
|
||||||
|
if($mentionee instanceof User)
|
||||||
|
(new MentionNotification($mentionee, $entity, $comment->getOwner(), strip_tags($comment->getText())))->emit();
|
||||||
|
|
||||||
$this->flashFail("succ", "Комментарий добавлен", "Ваш комментарий появится на странице.");
|
$this->flashFail("succ", "Комментарий добавлен", "Ваш комментарий появится на странице.");
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
<?php declare(strict_types=1);
|
<?php declare(strict_types=1);
|
||||||
namespace openvk\Web\Presenters;
|
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\Entities\Notifications\ClubModeratorNotification;
|
||||||
use openvk\Web\Models\Repositories\{Clubs, Users, Albums, Managers, Topics};
|
use openvk\Web\Models\Repositories\{Clubs, Users, Albums, Managers, Topics};
|
||||||
use Chandler\Security\Authenticator;
|
use Chandler\Security\Authenticator;
|
||||||
|
@ -191,7 +192,7 @@ final class GroupPresenter extends OpenVKPresenter
|
||||||
$this->willExecuteWriteAction();
|
$this->willExecuteWriteAction();
|
||||||
|
|
||||||
$club = $this->clubs->get($id);
|
$club = $this->clubs->get($id);
|
||||||
if(!$club->canBeModifiedBy($this->user->identity))
|
if(!$club || !$club->canBeModifiedBy($this->user->identity))
|
||||||
$this->notFound();
|
$this->notFound();
|
||||||
else
|
else
|
||||||
$this->template->club = $club;
|
$this->template->club = $club;
|
||||||
|
@ -250,6 +251,88 @@ final class GroupPresenter extends OpenVKPresenter
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function renderSetAvatar(int $id)
|
||||||
|
{
|
||||||
|
$photo = new Photo;
|
||||||
|
$club = $this->clubs->get($id);
|
||||||
|
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", "Неизвестная ошибка", "Не удалось сохранить фотографию.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$this->returnJson([
|
||||||
|
"url" => $photo->getURL(),
|
||||||
|
"id" => $photo->getPrettyId()
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
function renderEditBackdrop(int $id): void
|
||||||
|
{
|
||||||
|
$this->assertUserLoggedIn();
|
||||||
|
$this->willExecuteWriteAction();
|
||||||
|
|
||||||
|
$club = $this->clubs->get($id);
|
||||||
|
if(!$club || !$club->canBeModifiedBy($this->user->identity))
|
||||||
|
$this->notFound();
|
||||||
|
else
|
||||||
|
$this->template->club = $club;
|
||||||
|
|
||||||
|
if($_SERVER["REQUEST_METHOD"] !== "POST")
|
||||||
|
return;
|
||||||
|
|
||||||
|
if($this->postParam("subact") === "remove") {
|
||||||
|
$club->unsetBackDropPictures();
|
||||||
|
$club->save();
|
||||||
|
$this->flashFail("succ", tr("backdrop_succ_rem"), tr("backdrop_succ_desc")); # will exit
|
||||||
|
}
|
||||||
|
|
||||||
|
$pic1 = $pic2 = NULL;
|
||||||
|
try {
|
||||||
|
if($_FILES["backdrop1"]["error"] !== UPLOAD_ERR_NO_FILE)
|
||||||
|
$pic1 = Photo::fastMake($this->user->id, "Profile backdrop (system)", $_FILES["backdrop1"]);
|
||||||
|
|
||||||
|
if($_FILES["backdrop2"]["error"] !== UPLOAD_ERR_NO_FILE)
|
||||||
|
$pic2 = Photo::fastMake($this->user->id, "Profile backdrop (system)", $_FILES["backdrop2"]);
|
||||||
|
} catch(InvalidStateException $e) {
|
||||||
|
$this->flashFail("err", tr("backdrop_error_title"), tr("backdrop_error_no_media"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if($pic1 == $pic2 && is_null($pic1))
|
||||||
|
$this->flashFail("err", tr("backdrop_error_title"), tr("backdrop_error_no_media"));
|
||||||
|
|
||||||
|
$club->setBackDropPictures($pic1, $pic2);
|
||||||
|
$club->save();
|
||||||
|
$this->flashFail("succ", tr("backdrop_succ"), tr("backdrop_succ_desc"));
|
||||||
|
}
|
||||||
|
|
||||||
function renderStatistics(int $id): void
|
function renderStatistics(int $id): void
|
||||||
{
|
{
|
||||||
$this->assertUserLoggedIn();
|
$this->assertUserLoggedIn();
|
||||||
|
|
|
@ -57,6 +57,11 @@ final class MessengerPresenter extends OpenVKPresenter
|
||||||
$correspondent = $this->getCorrespondent($sel);
|
$correspondent = $this->getCorrespondent($sel);
|
||||||
if(!$correspondent)
|
if(!$correspondent)
|
||||||
$this->notFound();
|
$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->selId = $sel;
|
||||||
$this->template->correspondent = $correspondent;
|
$this->template->correspondent = $correspondent;
|
||||||
|
|
|
@ -254,6 +254,7 @@ abstract class OpenVKPresenter extends SimplePresenter
|
||||||
$cacheTime = 0; # Force no cache
|
$cacheTime = 0; # Force no cache
|
||||||
if($this->user->identity->onlineStatus() == 0 && !($this->user->identity->isDeleted() || $this->user->identity->isBanned())) {
|
if($this->user->identity->onlineStatus() == 0 && !($this->user->identity->isDeleted() || $this->user->identity->isBanned())) {
|
||||||
$this->user->identity->setOnline(time());
|
$this->user->identity->setOnline(time());
|
||||||
|
$this->user->identity->setClient_name(NULL);
|
||||||
$this->user->identity->save();
|
$this->user->identity->save();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -185,6 +185,18 @@ final class PhotosPresenter extends OpenVKPresenter
|
||||||
$this->renderPhoto($photo->getOwner(true)->getId(), $photo->getVirtualId());
|
$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
|
function renderEditPhoto(int $ownerId, int $photoId): void
|
||||||
{
|
{
|
||||||
$this->assertUserLoggedIn();
|
$this->assertUserLoggedIn();
|
||||||
|
|
|
@ -1,18 +1,28 @@
|
||||||
<?php declare(strict_types=1);
|
<?php declare(strict_types=1);
|
||||||
namespace openvk\Web\Presenters;
|
namespace openvk\Web\Presenters;
|
||||||
use openvk\Web\Models\Entities\{User, Club};
|
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;
|
use Chandler\Database\DatabaseConnection;
|
||||||
|
|
||||||
final class SearchPresenter extends OpenVKPresenter
|
final class SearchPresenter extends OpenVKPresenter
|
||||||
{
|
{
|
||||||
private $users;
|
private $users;
|
||||||
private $clubs;
|
private $clubs;
|
||||||
|
private $posts;
|
||||||
|
private $comments;
|
||||||
|
private $videos;
|
||||||
|
private $apps;
|
||||||
|
private $notes;
|
||||||
|
|
||||||
function __construct(Users $users, Clubs $clubs)
|
function __construct(Users $users, Clubs $clubs)
|
||||||
{
|
{
|
||||||
$this->users = $users;
|
$this->users = $users;
|
||||||
$this->clubs = $clubs;
|
$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();
|
parent::__construct();
|
||||||
}
|
}
|
||||||
|
@ -21,6 +31,8 @@ final class SearchPresenter extends OpenVKPresenter
|
||||||
{
|
{
|
||||||
$query = $this->queryParam("query") ?? "";
|
$query = $this->queryParam("query") ?? "";
|
||||||
$type = $this->queryParam("type") ?? "users";
|
$type = $this->queryParam("type") ?? "users";
|
||||||
|
$sorter = $this->queryParam("sort") ?? "id";
|
||||||
|
$invert = $this->queryParam("invert") == 1 ? "ASC" : "DESC";
|
||||||
$page = (int) ($this->queryParam("p") ?? 1);
|
$page = (int) ($this->queryParam("p") ?? 1);
|
||||||
|
|
||||||
$this->willExecuteWriteAction();
|
$this->willExecuteWriteAction();
|
||||||
|
@ -28,11 +40,58 @@ final class SearchPresenter extends OpenVKPresenter
|
||||||
$this->assertUserLoggedIn();
|
$this->assertUserLoggedIn();
|
||||||
|
|
||||||
# https://youtu.be/pSAWM5YuXx8
|
# 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.");
|
$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);
|
$iterator = $results->page($page);
|
||||||
$count = $results->size();
|
$count = $results->size();
|
||||||
|
|
||||||
|
|
|
@ -84,6 +84,9 @@ final class TopicsPresenter extends OpenVKPresenter
|
||||||
if($this->postParam("as_group") === "on" && $club->canBeModifiedBy($this->user->identity))
|
if($this->postParam("as_group") === "on" && $club->canBeModifiedBy($this->user->identity))
|
||||||
$flags |= 0b10000000;
|
$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 = new Topic;
|
||||||
$topic->setGroup($club->getId());
|
$topic->setGroup($club->getId());
|
||||||
$topic->setOwner($this->user->id);
|
$topic->setOwner($this->user->id);
|
||||||
|
@ -105,7 +108,7 @@ final class TopicsPresenter extends OpenVKPresenter
|
||||||
}
|
}
|
||||||
|
|
||||||
if($_FILES["_vid_attachment"]["error"] === UPLOAD_ERR_OK) {
|
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) {
|
} catch(ISE $ex) {
|
||||||
$this->flash("err", "Не удалось опубликовать комментарий", "Файл медиаконтента повреждён или слишком велик.");
|
$this->flash("err", "Не удалось опубликовать комментарий", "Файл медиаконтента повреждён или слишком велик.");
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
<?php declare(strict_types=1);
|
<?php declare(strict_types=1);
|
||||||
namespace openvk\Web\Presenters;
|
namespace openvk\Web\Presenters;
|
||||||
|
use Nette\InvalidStateException;
|
||||||
use openvk\Web\Util\Sms;
|
use openvk\Web\Util\Sms;
|
||||||
use openvk\Web\Themes\Themepacks;
|
use openvk\Web\Themes\Themepacks;
|
||||||
use openvk\Web\Models\Entities\{Photo, Post, EmailChangeVerification};
|
use openvk\Web\Models\Entities\{Photo, Post, EmailChangeVerification};
|
||||||
|
@ -38,6 +39,7 @@ final class UserPresenter extends OpenVKPresenter
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$this->template->albums = (new Albums)->getUserAlbums($user);
|
$this->template->albums = (new Albums)->getUserAlbums($user);
|
||||||
|
$this->template->avatarAlbum = (new Albums)->getUserAvatarAlbum($user);
|
||||||
$this->template->albumsCount = (new Albums)->getUserAlbumsCount($user);
|
$this->template->albumsCount = (new Albums)->getUserAlbumsCount($user);
|
||||||
$this->template->videos = (new Videos)->getByUser($user, 1, 2);
|
$this->template->videos = (new Videos)->getByUser($user, 1, 2);
|
||||||
$this->template->videosCount = (new Videos)->getUserVideosCount($user);
|
$this->template->videosCount = (new Videos)->getUserVideosCount($user);
|
||||||
|
@ -208,6 +210,30 @@ final class UserPresenter extends OpenVKPresenter
|
||||||
$user->setFav_Books(empty($this->postParam("fav_books")) ? NULL : ovk_proc_strtr($this->postParam("fav_books"), 300));
|
$user->setFav_Books(empty($this->postParam("fav_books")) ? NULL : ovk_proc_strtr($this->postParam("fav_books"), 300));
|
||||||
$user->setFav_Quote(empty($this->postParam("fav_quote")) ? NULL : ovk_proc_strtr($this->postParam("fav_quote"), 300));
|
$user->setFav_Quote(empty($this->postParam("fav_quote")) ? NULL : ovk_proc_strtr($this->postParam("fav_quote"), 300));
|
||||||
$user->setAbout(empty($this->postParam("about")) ? NULL : ovk_proc_strtr($this->postParam("about"), 300));
|
$user->setAbout(empty($this->postParam("about")) ? NULL : ovk_proc_strtr($this->postParam("about"), 300));
|
||||||
|
} elseif($_GET["act"] === "backdrop") {
|
||||||
|
if($this->postParam("subact") === "remove") {
|
||||||
|
$user->unsetBackDropPictures();
|
||||||
|
$user->save();
|
||||||
|
$this->flashFail("succ", tr("backdrop_succ_rem"), tr("backdrop_succ_desc")); # will exit
|
||||||
|
}
|
||||||
|
|
||||||
|
$pic1 = $pic2 = NULL;
|
||||||
|
try {
|
||||||
|
if($_FILES["backdrop1"]["error"] !== UPLOAD_ERR_NO_FILE)
|
||||||
|
$pic1 = Photo::fastMake($user->getId(), "Profile backdrop (system)", $_FILES["backdrop1"]);
|
||||||
|
|
||||||
|
if($_FILES["backdrop2"]["error"] !== UPLOAD_ERR_NO_FILE)
|
||||||
|
$pic2 = Photo::fastMake($user->getId(), "Profile backdrop (system)", $_FILES["backdrop2"]);
|
||||||
|
} catch(InvalidStateException $e) {
|
||||||
|
$this->flashFail("err", tr("backdrop_error_title"), tr("backdrop_error_no_media"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if($pic1 == $pic2 && is_null($pic1))
|
||||||
|
$this->flashFail("err", tr("backdrop_error_title"), tr("backdrop_error_no_media"));
|
||||||
|
|
||||||
|
$user->setBackDropPictures($pic1, $pic2);
|
||||||
|
$user->save();
|
||||||
|
$this->flashFail("succ", tr("backdrop_succ"), tr("backdrop_succ_desc"));
|
||||||
} elseif($_GET['act'] === "status") {
|
} elseif($_GET['act'] === "status") {
|
||||||
if(mb_strlen($this->postParam("status")) > 255) {
|
if(mb_strlen($this->postParam("status")) > 255) {
|
||||||
$statusLength = (string) mb_strlen($this->postParam("status"));
|
$statusLength = (string) mb_strlen($this->postParam("status"));
|
||||||
|
@ -235,7 +261,7 @@ final class UserPresenter extends OpenVKPresenter
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->template->mode = in_array($this->queryParam("act"), [
|
$this->template->mode = in_array($this->queryParam("act"), [
|
||||||
"main", "contacts", "interests", "avatar"
|
"main", "contacts", "interests", "avatar", "backdrop"
|
||||||
]) ? $this->queryParam("act")
|
]) ? $this->queryParam("act")
|
||||||
: "main";
|
: "main";
|
||||||
|
|
||||||
|
@ -276,7 +302,7 @@ final class UserPresenter extends OpenVKPresenter
|
||||||
$this->redirect($user->getURL());
|
$this->redirect($user->getURL());
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderSetAvatar(): void
|
function renderSetAvatar()
|
||||||
{
|
{
|
||||||
$this->assertUserLoggedIn();
|
$this->assertUserLoggedIn();
|
||||||
$this->willExecuteWriteAction();
|
$this->willExecuteWriteAction();
|
||||||
|
@ -296,8 +322,26 @@ final class UserPresenter extends OpenVKPresenter
|
||||||
$album->addPhoto($photo);
|
$album->addPhoto($photo);
|
||||||
$album->setEdited(time());
|
$album->setEdited(time());
|
||||||
$album->save();
|
$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
|
function renderSettings(): void
|
||||||
|
|
|
@ -6,6 +6,7 @@ use openvk\VKAPI\Exceptions\APIErrorException;
|
||||||
use openvk\Web\Models\Entities\{User, APIToken};
|
use openvk\Web\Models\Entities\{User, APIToken};
|
||||||
use openvk\Web\Models\Repositories\{Users, APITokens};
|
use openvk\Web\Models\Repositories\{Users, APITokens};
|
||||||
use lfkeitel\phptotp\{Base32, Totp};
|
use lfkeitel\phptotp\{Base32, Totp};
|
||||||
|
use WhichBrowser;
|
||||||
|
|
||||||
final class VKAPIPresenter extends OpenVKPresenter
|
final class VKAPIPresenter extends OpenVKPresenter
|
||||||
{
|
{
|
||||||
|
@ -98,20 +99,21 @@ final class VKAPIPresenter extends OpenVKPresenter
|
||||||
|
|
||||||
function renderPhotoUpload(string $signature): void
|
function renderPhotoUpload(string $signature): void
|
||||||
{
|
{
|
||||||
$secret = CHANDLER_ROOT_CONF["security"]["secret"];
|
$secret = CHANDLER_ROOT_CONF["security"]["secret"];
|
||||||
$computedSignature = hash_hmac("sha3-224", $_SERVER["QUERY_STRING"], $secret);
|
$queryString = rawurldecode($_SERVER["QUERY_STRING"]);
|
||||||
|
$computedSignature = hash_hmac("sha3-224", $queryString, $secret);
|
||||||
if(!(strlen($signature) == 56 && sodium_memcmp($signature, $computedSignature) == 0)) {
|
if(!(strlen($signature) == 56 && sodium_memcmp($signature, $computedSignature) == 0)) {
|
||||||
header("HTTP/1.1 422 Unprocessable Entity");
|
header("HTTP/1.1 422 Unprocessable Entity");
|
||||||
exit("Try harder <3");
|
exit("Try harder <3");
|
||||||
}
|
}
|
||||||
|
|
||||||
$data = unpack("vDOMAIN/Z10FIELD/vMF/vMP/PTIME/PUSER/PGROUP", base64_decode($_SERVER["QUERY_STRING"]));
|
$data = unpack("vDOMAIN/Z10FIELD/vMF/vMP/PTIME/PUSER/PGROUP", base64_decode($queryString));
|
||||||
if((time() - $data["TIME"]) > 600) {
|
if((time() - $data["TIME"]) > 600) {
|
||||||
header("HTTP/1.1 422 Unprocessable Entity");
|
header("HTTP/1.1 422 Unprocessable Entity");
|
||||||
exit("Expired");
|
exit("Expired");
|
||||||
}
|
}
|
||||||
|
|
||||||
$folder = __DIR__ . "../../tmp/api-storage/photos";
|
$folder = __DIR__ . "/../../tmp/api-storage/photos";
|
||||||
$maxSize = OPENVK_ROOT_CONF["openvk"]["preferences"]["uploads"]["api"]["maxFileSize"];
|
$maxSize = OPENVK_ROOT_CONF["openvk"]["preferences"]["uploads"]["api"]["maxFileSize"];
|
||||||
$maxFiles = OPENVK_ROOT_CONF["openvk"]["preferences"]["uploads"]["api"]["maxFilesPerDomain"];
|
$maxFiles = OPENVK_ROOT_CONF["openvk"]["preferences"]["uploads"]["api"]["maxFilesPerDomain"];
|
||||||
$usrFiles = sizeof(glob("$folder/$data[USER]_*.oct"));
|
$usrFiles = sizeof(glob("$folder/$data[USER]_*.oct"));
|
||||||
|
@ -195,19 +197,24 @@ final class VKAPIPresenter extends OpenVKPresenter
|
||||||
$identity = NULL;
|
$identity = NULL;
|
||||||
} else {
|
} else {
|
||||||
$token = (new APITokens)->getByCode($this->requestParam("access_token"));
|
$token = (new APITokens)->getByCode($this->requestParam("access_token"));
|
||||||
if(!$token)
|
if(!$token) {
|
||||||
$identity = NULL;
|
$identity = NULL;
|
||||||
else
|
} else {
|
||||||
$identity = $token->getUser();
|
$identity = $token->getUser();
|
||||||
|
$platform = $token->getPlatform();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(!is_null($identity) && $identity->isBanned())
|
||||||
|
$this->fail(18, "User account is deactivated", $object, $method);
|
||||||
|
|
||||||
$object = ucfirst(strtolower($object));
|
$object = ucfirst(strtolower($object));
|
||||||
$handlerClass = "openvk\\VKAPI\\Handlers\\$object";
|
$handlerClass = "openvk\\VKAPI\\Handlers\\$object";
|
||||||
if(!class_exists($handlerClass))
|
if(!class_exists($handlerClass))
|
||||||
$this->badMethod($object, $method);
|
$this->badMethod($object, $method);
|
||||||
|
|
||||||
$handler = new $handlerClass($identity);
|
$handler = new $handlerClass($identity, $platform);
|
||||||
if(!is_callable([$handler, $method]))
|
if(!is_callable([$handler, $method]))
|
||||||
$this->badMethod($object, $method);
|
$this->badMethod($object, $method);
|
||||||
|
|
||||||
|
@ -274,8 +281,11 @@ final class VKAPIPresenter extends OpenVKPresenter
|
||||||
$this->fail(28, "Invalid 2FA code", "internal", "acquireToken");
|
$this->fail(28, "Invalid 2FA code", "internal", "acquireToken");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$platform = $this->requestParam("client_name");
|
||||||
|
|
||||||
$token = new APIToken;
|
$token = new APIToken;
|
||||||
$token->setUser($user);
|
$token->setUser($user);
|
||||||
|
$token->setPlatform($platform ?? (new WhichBrowser\Parser(getallheaders()))->toString());
|
||||||
$token->save();
|
$token->save();
|
||||||
|
|
||||||
$payload = json_encode([
|
$payload = json_encode([
|
||||||
|
|
|
@ -56,6 +56,9 @@ final class VideosPresenter extends OpenVKPresenter
|
||||||
{
|
{
|
||||||
$this->assertUserLoggedIn();
|
$this->assertUserLoggedIn();
|
||||||
$this->willExecuteWriteAction();
|
$this->willExecuteWriteAction();
|
||||||
|
|
||||||
|
if(OPENVK_ROOT_CONF['openvk']['preferences']['videos']['disableUploading'])
|
||||||
|
$this->flashFail("err", tr("error"), "Video uploads are disabled by the system administrator.");
|
||||||
|
|
||||||
if($_SERVER["REQUEST_METHOD"] === "POST") {
|
if($_SERVER["REQUEST_METHOD"] === "POST") {
|
||||||
if(!empty($this->postParam("name"))) {
|
if(!empty($this->postParam("name"))) {
|
||||||
|
|
|
@ -2,8 +2,8 @@
|
||||||
namespace openvk\Web\Presenters;
|
namespace openvk\Web\Presenters;
|
||||||
use openvk\Web\Models\Exceptions\TooMuchOptionsException;
|
use openvk\Web\Models\Exceptions\TooMuchOptionsException;
|
||||||
use openvk\Web\Models\Entities\{Poll, Post, Photo, Video, Club, User};
|
use openvk\Web\Models\Entities\{Poll, Post, Photo, Video, Club, User};
|
||||||
use openvk\Web\Models\Entities\Notifications\{RepostNotification, WallPostNotification};
|
use openvk\Web\Models\Entities\Notifications\{MentionNotification, RepostNotification, WallPostNotification};
|
||||||
use openvk\Web\Models\Repositories\{Posts, Users, Clubs, Albums};
|
use openvk\Web\Models\Repositories\{Posts, Users, Clubs, Albums, Notes};
|
||||||
use Chandler\Database\DatabaseConnection;
|
use Chandler\Database\DatabaseConnection;
|
||||||
use Nette\InvalidStateException as ISE;
|
use Nette\InvalidStateException as ISE;
|
||||||
use Bhaktaraz\RSSGenerator\Item;
|
use Bhaktaraz\RSSGenerator\Item;
|
||||||
|
@ -229,6 +229,9 @@ final class WallPresenter extends OpenVKPresenter
|
||||||
if(!$canPost)
|
if(!$canPost)
|
||||||
$this->flashFail("err", tr("not_enough_permissions"), tr("not_enough_permissions_comment"));
|
$this->flashFail("err", tr("not_enough_permissions"), tr("not_enough_permissions_comment"));
|
||||||
|
|
||||||
|
if($_FILES["_vid_attachment"] && OPENVK_ROOT_CONF['openvk']['preferences']['videos']['disableUploading'])
|
||||||
|
$this->flashFail("err", tr("error"), "Video uploads are disabled by the system administrator.");
|
||||||
|
|
||||||
$anon = OPENVK_ROOT_CONF["openvk"]["preferences"]["wall"]["anonymousPosting"]["enable"];
|
$anon = OPENVK_ROOT_CONF["openvk"]["preferences"]["wall"]["anonymousPosting"]["enable"];
|
||||||
if($wallOwner instanceof Club && $this->postParam("as_group") === "on" && $this->postParam("force_sign") !== "on" && $anon) {
|
if($wallOwner instanceof Club && $this->postParam("as_group") === "on" && $this->postParam("force_sign") !== "on" && $anon) {
|
||||||
$manager = $wallOwner->getManager($this->user->identity);
|
$manager = $wallOwner->getManager($this->user->identity);
|
||||||
|
@ -258,7 +261,7 @@ final class WallPresenter extends OpenVKPresenter
|
||||||
}
|
}
|
||||||
|
|
||||||
if($_FILES["_vid_attachment"]["error"] === UPLOAD_ERR_OK)
|
if($_FILES["_vid_attachment"]["error"] === UPLOAD_ERR_OK)
|
||||||
$video = Video::fastMake($this->user->id, $this->postParam("text"), $_FILES["_vid_attachment"], $anon);
|
$video = Video::fastMake($this->user->id, $_FILES["_vid_attachment"]["name"], $this->postParam("text"), $_FILES["_vid_attachment"], $anon);
|
||||||
} catch(\DomainException $ex) {
|
} catch(\DomainException $ex) {
|
||||||
$this->flashFail("err", tr("failed_to_publish_post"), tr("media_file_corrupted"));
|
$this->flashFail("err", tr("failed_to_publish_post"), tr("media_file_corrupted"));
|
||||||
} catch(ISE $ex) {
|
} catch(ISE $ex) {
|
||||||
|
@ -275,8 +278,22 @@ final class WallPresenter extends OpenVKPresenter
|
||||||
} catch(\UnexpectedValueException $e) {
|
} catch(\UnexpectedValueException $e) {
|
||||||
$this->flashFail("err", tr("failed_to_publish_post"), "Poll format invalid");
|
$this->flashFail("err", tr("failed_to_publish_post"), "Poll format invalid");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$note = NULL;
|
||||||
|
|
||||||
|
if(!is_null($this->postParam("note")) && $this->postParam("note") != "none") {
|
||||||
|
$note = (new Notes)->get((int)$this->postParam("note"));
|
||||||
|
|
||||||
|
if(!$note || $note->isDeleted() || $note->getOwner()->getId() != $this->user->id) {
|
||||||
|
$this->flashFail("err", tr("error"), tr("error_attaching_note"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if($note->getOwner()->getPrivacySetting("notes.read") < 1) {
|
||||||
|
$this->flashFail("err", " ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if(empty($this->postParam("text")) && !$photo && !$video && !$poll)
|
if(empty($this->postParam("text")) && !$photo && !$video && !$poll && !$note)
|
||||||
$this->flashFail("err", tr("failed_to_publish_post"), tr("post_is_empty_or_too_big"));
|
$this->flashFail("err", tr("failed_to_publish_post"), tr("post_is_empty_or_too_big"));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -301,10 +318,22 @@ final class WallPresenter extends OpenVKPresenter
|
||||||
|
|
||||||
if(!is_null($poll))
|
if(!is_null($poll))
|
||||||
$post->attach($poll);
|
$post->attach($poll);
|
||||||
|
|
||||||
|
if(!is_null($note))
|
||||||
|
$post->attach($note);
|
||||||
|
|
||||||
if($wall > 0 && $wall !== $this->user->identity->getId())
|
if($wall > 0 && $wall !== $this->user->identity->getId())
|
||||||
(new WallPostNotification($wallOwner, $post, $this->user->identity))->emit();
|
(new WallPostNotification($wallOwner, $post, $this->user->identity))->emit();
|
||||||
|
|
||||||
|
$excludeMentions = [$this->user->identity->getId()];
|
||||||
|
if($wall > 0)
|
||||||
|
$excludeMentions[] = $wall;
|
||||||
|
|
||||||
|
$mentions = iterator_to_array($post->resolveMentions($excludeMentions));
|
||||||
|
foreach($mentions as $mentionee)
|
||||||
|
if($mentionee instanceof User)
|
||||||
|
(new MentionNotification($mentionee, $post, $post->getOwner(), strip_tags($post->getText())))->emit();
|
||||||
|
|
||||||
$this->redirect($wallOwner->getURL());
|
$this->redirect($wallOwner->getURL());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -354,21 +383,52 @@ final class WallPresenter extends OpenVKPresenter
|
||||||
$this->assertNoCSRF();
|
$this->assertNoCSRF();
|
||||||
|
|
||||||
$post = $this->posts->getPostById($wall, $post_id);
|
$post = $this->posts->getPostById($wall, $post_id);
|
||||||
if(!$post || $post->isDeleted()) $this->notFound();
|
|
||||||
|
if(!$post || $post->isDeleted())
|
||||||
|
$this->notFound();
|
||||||
|
|
||||||
|
$where = $this->postParam("type") ?? "wall";
|
||||||
|
$groupId = NULL;
|
||||||
|
$flags = 0;
|
||||||
|
|
||||||
|
if($where == "group")
|
||||||
|
$groupId = $this->postParam("groupId");
|
||||||
|
|
||||||
if(!is_null($this->user)) {
|
if(!is_null($this->user)) {
|
||||||
$nPost = new Post;
|
$nPost = new Post;
|
||||||
$nPost->setOwner($this->user->id);
|
|
||||||
$nPost->setWall($this->user->id);
|
if($where == "wall") {
|
||||||
|
$nPost->setOwner($this->user->id);
|
||||||
|
$nPost->setWall($this->user->id);
|
||||||
|
} elseif($where == "group") {
|
||||||
|
$nPost->setOwner($this->user->id);
|
||||||
|
$club = (new Clubs)->get((int)$groupId);
|
||||||
|
|
||||||
|
if(!$club || !$club->canBeModifiedBy($this->user->identity))
|
||||||
|
$this->notFound();
|
||||||
|
|
||||||
|
if($this->postParam("asGroup") == 1)
|
||||||
|
$flags |= 0b10000000;
|
||||||
|
|
||||||
|
if($this->postParam("signed") == 1)
|
||||||
|
$flags |= 0b01000000;
|
||||||
|
|
||||||
|
$nPost->setWall($groupId * -1);
|
||||||
|
}
|
||||||
|
|
||||||
$nPost->setContent($this->postParam("text"));
|
$nPost->setContent($this->postParam("text"));
|
||||||
|
$nPost->setFlags($flags);
|
||||||
$nPost->save();
|
$nPost->save();
|
||||||
|
|
||||||
$nPost->attach($post);
|
$nPost->attach($post);
|
||||||
|
|
||||||
if($post->getOwner(false)->getId() !== $this->user->identity->getId() && !($post->getOwner() instanceof Club))
|
if($post->getOwner(false)->getId() !== $this->user->identity->getId() && !($post->getOwner() instanceof Club))
|
||||||
(new RepostNotification($post->getOwner(false), $post, $this->user->identity))->emit();
|
(new RepostNotification($post->getOwner(false), $post, $this->user->identity))->emit();
|
||||||
};
|
};
|
||||||
|
|
||||||
$this->returnJson(["wall_owner" => $this->user->identity->getId()]);
|
$this->returnJson([
|
||||||
|
"wall_owner" => $where == "wall" ? $this->user->identity->getId() : $groupId * -1
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderDelete(int $wall, int $post_id): void
|
function renderDelete(int $wall, int $post_id): void
|
||||||
|
|
|
@ -49,6 +49,34 @@
|
||||||
<div class="notifications_global_wrap"></div>
|
<div class="notifications_global_wrap"></div>
|
||||||
<div class="dimmer"></div>
|
<div class="dimmer"></div>
|
||||||
|
|
||||||
|
<div class="articleView">
|
||||||
|
<a id="articleCloseButton" class="button" href="javascript:void(u('body').removeClass('article'));">{_close}</a>
|
||||||
|
<div class="articleView_container">
|
||||||
|
<div class="articleView_info">
|
||||||
|
<div class="articleView_author">
|
||||||
|
<img id="articleAuthorAva" src="" />
|
||||||
|
<div>
|
||||||
|
<span><a id="articleAuthorName"></a></span>
|
||||||
|
<time id="articleTime"></time>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="articleView_link">
|
||||||
|
<a id="articleLink" href="/" class="button">{_aw_legacy_ui}</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="articleView_text" id="articleText">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{if isset($backdrops) && !is_null($backdrops)}
|
||||||
|
<div id="backdrop" style="background-image: url('{$backdrops[0]|noescape}'), url('{$backdrops[1]|noescape}');">
|
||||||
|
<div id="backdropDripper"></div>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
<div class="toTop">
|
<div class="toTop">
|
||||||
⬆ {_to_top}
|
⬆ {_to_top}
|
||||||
</div>
|
</div>
|
||||||
|
@ -64,28 +92,66 @@
|
||||||
<a href="/logout?hash={urlencode($csrfToken)}">{_header_log_out}</a>
|
<a href="/logout?hash={urlencode($csrfToken)}">{_header_log_out}</a>
|
||||||
</div>
|
</div>
|
||||||
{else}
|
{else}
|
||||||
<div class="link">
|
<div class="link dec">
|
||||||
<a href="/">{_header_home}</a>
|
<a href="/">{_header_home}</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="link">
|
<div class="link dec">
|
||||||
<a href="/search?type=groups">{_header_groups}</a>
|
<a href="/search?type=groups">{_header_groups}</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="link">
|
<div class="link dec">
|
||||||
<a href="/search">{_header_search}</a>
|
<a href="/search">{_header_search}</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="link">
|
<div class="link dec">
|
||||||
<a href="/invite">{_header_invite}</a>
|
<a href="/invite">{_header_invite}</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="link">
|
<div class="link dec">
|
||||||
<a href="/support">{_header_help} <b n:if="$ticketAnsweredCount > 0">({$ticketAnsweredCount})</b></a>
|
<a href="/support">{_header_help} <b n:if="$ticketAnsweredCount > 0">({$ticketAnsweredCount})</b></a>
|
||||||
</div>
|
</div>
|
||||||
<div class="link">
|
<div class="link dec">
|
||||||
<a href="/logout?hash={urlencode($csrfToken)}">{_header_log_out}</a>
|
<a href="/logout?hash={urlencode($csrfToken)}">{_header_log_out}</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="link">
|
{var $atSearch = str_contains($_SERVER['REQUEST_URI'], "/search")}
|
||||||
<form action="/search" method="get">
|
<div id="srch" class="{if $atSearch}nodivider{else}link{/if}">
|
||||||
<input type="search" name="query" placeholder="{_header_search}" style="height: 20px;background: url('/assets/packages/static/openvk/img/search_icon.png') no-repeat 3px 4px; background-color: #fff; padding-left: 18px;width: 120px;" title="{_header_search} [Alt+Shift+F]" accesskey="f" />
|
|
||||||
</form>
|
{if !$atSearch}
|
||||||
|
<form action="/search" method="get" id="searcher" style="position:relative;">
|
||||||
|
<input autocomplete="off" id="searchInput" oninput="checkSearchTips()" onfocus="expandSearch()" onblur="decreaseSearch()" class="sr" type="search" name="query" placeholder="{_header_search}" style="height: 20px;background: url('/assets/packages/static/openvk/img/search_icon.png') no-repeat 3px 4px; background-color: #fff; padding-left: 18px;width: 120px;" title="{_header_search} [Alt+Shift+F]" accesskey="f" />
|
||||||
|
<select onchange="checkSearchTips()" id="typer" name="type" class="whatFind" style="display:none;top: 0px;">
|
||||||
|
<option value="users">{_s_by_people}</option>
|
||||||
|
<option value="groups">{_s_by_groups}</option>
|
||||||
|
<option value="posts">{_s_by_posts}</option>
|
||||||
|
<option value="comments">{_s_by_comments}</option>
|
||||||
|
<option value="videos">{_s_by_videos}</option>
|
||||||
|
<option value="apps">{_s_by_apps}</option>
|
||||||
|
</select>
|
||||||
|
</form>
|
||||||
|
<div class="searchTips" id="srcht" hidden>
|
||||||
|
<table style="border:none;border-spacing: 0;">
|
||||||
|
<tbody id="srchrr">
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
{else}
|
||||||
|
<form action="/search" method="get" id="searcher" style="margin-top: -1px;position:relative;">
|
||||||
|
<input id="searchInput" value="{$_GET['query'] ?? ''}" type="search" class="sr" name="query" placeholder="{_header_search}" style="height: 20px; background-color: #fff; padding-left: 6px;width: 555px;" title="{_header_search} [Alt+Shift+F]" accesskey="f" />
|
||||||
|
<select name="type" class="whatFind">
|
||||||
|
<option value="users" {if str_contains($_SERVER['REQUEST_URI'], "type=users")}selected{/if}>{_s_by_people}</option>
|
||||||
|
<option value="groups" {if str_contains($_SERVER['REQUEST_URI'], "type=groups")}selected{/if}>{_s_by_groups}</option>
|
||||||
|
<option value="posts" {if str_contains($_SERVER['REQUEST_URI'], "type=posts")}selected{/if}>{_s_by_posts}</option>
|
||||||
|
<option value="comments" {if str_contains($_SERVER['REQUEST_URI'], "type=comments")}selected{/if}>{_s_by_comments}</option>
|
||||||
|
<option value="videos" {if str_contains($_SERVER['REQUEST_URI'], "type=videos")}selected{/if}>{_s_by_videos}</option>
|
||||||
|
<option value="apps" {if str_contains($_SERVER['REQUEST_URI'], "type=apps")}selected{/if}>{_s_by_apps}</option>
|
||||||
|
</select>
|
||||||
|
<button class="searchBtn"><span style="color:white;font-weight: 600;font-size:12px;">{_header_search}</span></button>
|
||||||
|
</form>
|
||||||
|
<script>
|
||||||
|
let els = document.querySelectorAll("div.dec")
|
||||||
|
for(const element of els)
|
||||||
|
{
|
||||||
|
element.style.display = "none"
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
{else}
|
{else}
|
||||||
|
@ -163,7 +229,7 @@
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<div class="floating_sidebar">
|
<div class="floating_sidebar">
|
||||||
<a class="minilink" href="/friends{$thisUser->getId()}">
|
<a id="minilink-friends" class="minilink" href="/friends{$thisUser->getId()}">
|
||||||
<object type="internal/link" n:if="$thisUser->getFollowersCount() > 0">
|
<object type="internal/link" n:if="$thisUser->getFollowersCount() > 0">
|
||||||
<div class="counter">
|
<div class="counter">
|
||||||
+{$thisUser->getFollowersCount()}
|
+{$thisUser->getFollowersCount()}
|
||||||
|
@ -171,10 +237,10 @@
|
||||||
</object>
|
</object>
|
||||||
<img src="/assets/packages/static/openvk/img/friends.svg">
|
<img src="/assets/packages/static/openvk/img/friends.svg">
|
||||||
</a>
|
</a>
|
||||||
<a class="minilink" href="/albums{$thisUser->getId()}">
|
<a id="minilink-albums" class="minilink" href="/albums{$thisUser->getId()}">
|
||||||
<img src="/assets/packages/static/openvk/img/photos.svg">
|
<img src="/assets/packages/static/openvk/img/photos.svg">
|
||||||
</a>
|
</a>
|
||||||
<a class="minilink" href="/im">
|
<a id="minilink-messenger" class="minilink" href="/im">
|
||||||
<object type="internal/link" n:if="$thisUser->getUnreadMessagesCount() > 0">
|
<object type="internal/link" n:if="$thisUser->getUnreadMessagesCount() > 0">
|
||||||
<div class="counter">
|
<div class="counter">
|
||||||
+{$thisUser->getUnreadMessagesCount()}
|
+{$thisUser->getUnreadMessagesCount()}
|
||||||
|
@ -182,10 +248,10 @@
|
||||||
</object>
|
</object>
|
||||||
<img src="/assets/packages/static/openvk/img/messages.svg">
|
<img src="/assets/packages/static/openvk/img/messages.svg">
|
||||||
</a>
|
</a>
|
||||||
<a class="minilink" href="/groups{$thisUser->getId()}">
|
<a id="minilink-groups" class="minilink" href="/groups{$thisUser->getId()}">
|
||||||
<img src="/assets/packages/static/openvk/img/groups.svg">
|
<img src="/assets/packages/static/openvk/img/groups.svg">
|
||||||
</a>
|
</a>
|
||||||
<a class="minilink" href="/notifications">
|
<a id="minilink-notifications" class="minilink" href="/notifications">
|
||||||
<object type="internal/link" n:if="$thisUser->getNotificationsCount() > 0">
|
<object type="internal/link" n:if="$thisUser->getNotificationsCount() > 0">
|
||||||
<div class="counter">
|
<div class="counter">
|
||||||
+{$thisUser->getNotificationsCount()}
|
+{$thisUser->getNotificationsCount()}
|
||||||
|
@ -212,9 +278,9 @@
|
||||||
<input id="password" type="password" name="password" required />
|
<input id="password" type="password" name="password" required />
|
||||||
<input type="hidden" name="jReturnTo" value="{$_SERVER['REQUEST_URI']}" />
|
<input type="hidden" name="jReturnTo" value="{$_SERVER['REQUEST_URI']}" />
|
||||||
<input type="hidden" name="hash" value="{$csrfToken}" />
|
<input type="hidden" name="hash" value="{$csrfToken}" />
|
||||||
<input type="submit" value="{_log_in}" class="button" style="display: inline-block;" />
|
<input type="submit" value="{_log_in}" class="button" style="display: inline-block; font-family: Tahoma" />
|
||||||
<a href="/reg" class="button" style="display: inline-block;">{_registration}</a><br><br>
|
<a href="/reg"><input type="button" value="{_registration}" class="button" style="font-family: Tahoma" /></a><br><br>
|
||||||
<a href="/restore">{_forgot_password}</a>
|
{if !OPENVK_ROOT_CONF['openvk']['preferences']['security']['disablePasswordRestoring']}<a href="/restore">{_forgot_password}</a>{/if}
|
||||||
</form>
|
</form>
|
||||||
{/ifset}
|
{/ifset}
|
||||||
</div>
|
</div>
|
||||||
|
@ -260,6 +326,7 @@
|
||||||
|
|
||||||
<div class="navigation_footer">
|
<div class="navigation_footer">
|
||||||
<a href="/about" class="link">{_footer_about_instance}</a>
|
<a href="/about" class="link">{_footer_about_instance}</a>
|
||||||
|
<a href="/terms" class="link">{_footer_rules}</a>
|
||||||
<a href="/blog" class="link">{_footer_blog}</a>
|
<a href="/blog" class="link">{_footer_blog}</a>
|
||||||
<a href="/support" class="link">{_footer_help}</a>
|
<a href="/support" class="link">{_footer_help}</a>
|
||||||
<a href="/dev" target="_blank" class="link">{_footer_developers}</a>
|
<a href="/dev" target="_blank" class="link">{_footer_developers}</a>
|
||||||
|
@ -288,6 +355,7 @@
|
||||||
{script "js/messagebox.js"}
|
{script "js/messagebox.js"}
|
||||||
{script "js/notifications.js"}
|
{script "js/notifications.js"}
|
||||||
{script "js/scroll.js"}
|
{script "js/scroll.js"}
|
||||||
|
{script "js/player.js"}
|
||||||
{script "js/al_wall.js"}
|
{script "js/al_wall.js"}
|
||||||
{script "js/al_api.js"}
|
{script "js/al_api.js"}
|
||||||
{script "js/al_mentions.js"}
|
{script "js/al_mentions.js"}
|
||||||
|
@ -304,6 +372,8 @@
|
||||||
</script>
|
</script>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
|
<script>bsdnHydrate();</script>
|
||||||
|
|
||||||
<script n:if="OPENVK_ROOT_CONF['openvk']['telemetry']['plausible']['enable']" async defer data-domain="{php echo OPENVK_ROOT_CONF['openvk']['telemetry']['plausible']['domain']}" src="{php echo OPENVK_ROOT_CONF['openvk']['telemetry']['plausible']['server']}js/plausible.js"></script>
|
<script n:if="OPENVK_ROOT_CONF['openvk']['telemetry']['plausible']['enable']" async defer data-domain="{php echo OPENVK_ROOT_CONF['openvk']['telemetry']['plausible']['domain']}" src="{php echo OPENVK_ROOT_CONF['openvk']['telemetry']['plausible']['server']}js/plausible.js"></script>
|
||||||
|
|
||||||
<script n:if="OPENVK_ROOT_CONF['openvk']['telemetry']['piwik']['enable']">
|
<script n:if="OPENVK_ROOT_CONF['openvk']['telemetry']['piwik']['enable']">
|
||||||
|
|
|
@ -74,6 +74,6 @@
|
||||||
|
|
||||||
<h4>{_rules}</h4>
|
<h4>{_rules}</h4>
|
||||||
<div style="margin-top: 16px;">
|
<div style="margin-top: 16px;">
|
||||||
{presenter "openvk!Support->knowledgeBaseArticle", "rules"}
|
{tr("about_watch_rules", "/terms")|noescape}
|
||||||
</div>
|
</div>
|
||||||
{/block}
|
{/block}
|
||||||
|
|
|
@ -7,6 +7,9 @@
|
||||||
|
|
||||||
{block content}
|
{block content}
|
||||||
{presenter "openvk!Support->knowledgeBaseArticle", "about"}
|
{presenter "openvk!Support->knowledgeBaseArticle", "about"}
|
||||||
|
|
||||||
|
<a href="/tour"><div class="tour" onmouseover="this.style.backgroundColor='#FCFBF5'" onmouseout="this.style.backgroundColor='#F9F6E7'" style="background-color: rgb(249, 246, 231);"><b>{_tour_title}</b><div>{_tour_promo}</div></div></a><br>
|
||||||
|
|
||||||
<center>
|
<center>
|
||||||
<a class="button" style="margin-right: 5px;cursor: pointer;" href="/login">{_log_in}</a>
|
<a class="button" style="margin-right: 5px;cursor: pointer;" href="/login">{_log_in}</a>
|
||||||
<a class="button" style="cursor: pointer;" href="/reg">{_registration}</a>
|
<a class="button" style="cursor: pointer;" href="/reg">{_registration}</a>
|
||||||
|
|
515
Web/Presenters/templates/About/Tour.xml
Executable file
515
Web/Presenters/templates/About/Tour.xml
Executable file
|
@ -0,0 +1,515 @@
|
||||||
|
{extends "../@layout.xml"}
|
||||||
|
{block title}{_tour_title}{/block}
|
||||||
|
|
||||||
|
{block header}
|
||||||
|
{_tour_title}
|
||||||
|
{/block}
|
||||||
|
|
||||||
|
{block content}
|
||||||
|
|
||||||
|
{css "css/tour.css"}
|
||||||
|
<div id="tour">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div class="rightNav">
|
||||||
|
<h1>{_tour_title}</h1>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div class="rightLinks">
|
||||||
|
<div class="tab">
|
||||||
|
<button class="tablinks" onclick="eurotour(event, 'start')" id="defaultOpen"><div class="tabicon"><img src="assets/packages/static/openvk/img/icons/1.svg" width="16" height="16"></div>{_tour_section_1|noescape}</button>
|
||||||
|
<button class="tablinks" onclick="eurotour(event, 'profile')"><div class="tabicon"><img src="assets/packages/static/openvk/img/icons/2.svg" width="16" height="16"></div>{_tour_section_2|noescape}</button>
|
||||||
|
<button class="tablinks" onclick="eurotour(event, 'photos')"><div class="tabicon"><img src="assets/packages/static/openvk/img/icons/3.svg" width="16" height="16"></div>{_tour_section_3|noescape}</button>
|
||||||
|
<button class="tablinks" onclick="eurotour(event, 'search')"><div class="tabicon"><img src="assets/packages/static/openvk/img/icons/4.svg" width="16" height="16"></div>{_tour_section_4|noescape}</button>
|
||||||
|
<button class="tablinks" onclick="eurotour(event, 'videos')"><div class="tabicon"><img src="assets/packages/static/openvk/img/icons/5.svg" width="16" height="16"></div>{_tour_section_5|noescape}</button>
|
||||||
|
<button class="tablinks" onclick="eurotour(event, 'audios')"><div class="tabicon"><img src="assets/packages/static/openvk/img/icons/6.svg" width="16" height="16"></div>{_tour_section_6|noescape}</button>
|
||||||
|
<button class="tablinks" onclick="eurotour(event, 'news')"><div class="tabicon"><img src="assets/packages/static/openvk/img/icons/7.svg" width="16" height="16"></div>{_tour_section_7|noescape}</button>
|
||||||
|
<button class="tablinks" onclick="eurotour(event, 'news_global')"><div class="tabicon"><img src="assets/packages/static/openvk/img/icons/8.svg" width="16" height="16"></div>{_tour_section_8|noescape}</button>
|
||||||
|
<button class="tablinks" onclick="eurotour(event, 'groups')"><div class="tabicon"><img src="assets/packages/static/openvk/img/icons/9.svg" width="16" height="16"></div>{_tour_section_9|noescape}</button>
|
||||||
|
<button class="tablinks" onclick="eurotour(event, 'events')"><div class="tabicon"><img src="assets/packages/static/openvk/img/icons/10.svg" width="16" height="16"></div>{_tour_section_10|noescape}</button>
|
||||||
|
<button class="tablinks" onclick="eurotour(event, 'themes')"><div class="tabicon"><img src="assets/packages/static/openvk/img/icons/11.svg" width="16" height="16"></div>{_tour_section_11|noescape}</button>
|
||||||
|
<button class="tablinks" onclick="eurotour(event, 'customization')"><div class="tabicon"><img src="assets/packages/static/openvk/img/icons/12.svg" width="16" height="16"></div>{_tour_section_12|noescape}</button>
|
||||||
|
<button class="tablinks" onclick="eurotour(event, 'vouchers')"><div class="tabicon"><img src="assets/packages/static/openvk/img/icons/13.svg" width="16" height="16"></div>{_tour_section_13|noescape}</button>
|
||||||
|
<button class="tablinks" onclick="eurotour(event, 'mobile')"><div class="tabicon"><img src="assets/packages/static/openvk/img/icons/14.svg" width="16" height="16"></div>{_tour_section_14|noescape}</button>
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="rightNav" n:if="!isset($thisUser)">
|
||||||
|
<h1>{_reg_title|noescape}</h1>
|
||||||
|
<div class="rightLinks">
|
||||||
|
<div>{_reg_text|noescape}</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h1>{_ifnotlike_title|noescape}</h1>
|
||||||
|
<div class="rightLinks">
|
||||||
|
<div>{_ifnotlike_text|noescape}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<div id="start" class="tabcontent">
|
||||||
|
<h2>{_tour_section_1_title_1|noescape}</h2>
|
||||||
|
<ul class="listing">
|
||||||
|
<li><span>{_tour_section_1_text_1|noescape}</span></li>
|
||||||
|
<li><span>{_tour_section_1_text_2|noescape}</span></li>
|
||||||
|
<li><span>{_tour_section_1_text_3|noescape}</span></li>
|
||||||
|
</ul>
|
||||||
|
<img src="assets/packages/static/openvk/img/tour/reg.png" width="440">
|
||||||
|
<p class="big">{_tour_section_1_bottom_text_1|noescape}</p>
|
||||||
|
<div style="margin-top:10px; padding-left:175px">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<br>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="profile" class="tabcontent">
|
||||||
|
<h2>{_tour_section_2_title_1|noescape}</h2>
|
||||||
|
<ul class="listing">
|
||||||
|
<li><span>{_tour_section_2_text_1_1|noescape}</span></li>
|
||||||
|
<li><span>{_tour_section_2_text_1_2|noescape}</span></li>
|
||||||
|
<li><span>{_tour_section_2_text_1_3|noescape}</span></li>
|
||||||
|
</ul>
|
||||||
|
<img src="assets/packages/static/openvk/img/tour/profile.png" width="440">
|
||||||
|
<p class="big">{_tour_section_2_bottom_text_1|noescape}</p>
|
||||||
|
<h2>{_tour_section_2_title_2|noescape}</h2>
|
||||||
|
<ul class="listing">
|
||||||
|
<li><span>{_tour_section_2_text_2_1|noescape}</span></li>
|
||||||
|
<li><span>{_tour_section_2_text_2_2|noescape}</span></li>
|
||||||
|
<li><span>{_tour_section_2_text_2_3|noescape}</span></li>
|
||||||
|
</ul>
|
||||||
|
<img src="assets/packages/static/openvk/img/tour/privacy.png" width="440">
|
||||||
|
|
||||||
|
<h2>{_tour_section_2_title_3|noescape}</h2>
|
||||||
|
<ul class="listing">
|
||||||
|
<li><span>{_tour_section_2_text_3_1|noescape}</span></li>
|
||||||
|
<li><span>{_tour_section_2_text_3_2|noescape}</span></li>
|
||||||
|
</ul>
|
||||||
|
<center><img src="assets/packages/static/openvk/img/tour/adres_ff.jpg"></center>
|
||||||
|
<ul class="listing">
|
||||||
|
|
||||||
|
<li><span>{_tour_section_2_text_3_3|noescape}</span></li>
|
||||||
|
<li><span>{_tour_section_2_text_3_4|noescape}</span></li>
|
||||||
|
</ul>
|
||||||
|
<img src="assets/packages/static/openvk/img/tour/adres3.png" width="440">
|
||||||
|
<center><img src="assets/packages/static/openvk/img/tour/adres_ff_tohru.jpg"></center>
|
||||||
|
<br>
|
||||||
|
<i><p class="big">{_tour_section_2_bottom_text_2|noescape}</p></p></i>
|
||||||
|
|
||||||
|
<h2>{_tour_section_2_title_4|noescape}</h2>
|
||||||
|
<img src="assets/packages/static/openvk/img/tour/wall.png" width="440">
|
||||||
|
|
||||||
|
|
||||||
|
<br>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="photos" class="tabcontent">
|
||||||
|
<h2>{_tour_section_3_title_1|noescape}</h2>
|
||||||
|
<ul class="listing">
|
||||||
|
<li><span>{_tour_section_3_text_1|noescape}</span></li>
|
||||||
|
<li><span>{_tour_section_3_text_2|noescape}</span></li>
|
||||||
|
<li><span>{_tour_section_3_text_3|noescape}</span></li>
|
||||||
|
</ul>
|
||||||
|
<img src="assets/packages/static/openvk/img/tour/photos.png" width="440">
|
||||||
|
<img src="assets/packages/static/openvk/img/tour/photos2.png" width="440">
|
||||||
|
<p class="big">{_tour_section_3_bottom_text_1|noescape}</p>
|
||||||
|
|
||||||
|
|
||||||
|
<br>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="search" class="tabcontent">
|
||||||
|
<h2>{_tour_section_4_title_1|noescape}</h2>
|
||||||
|
|
||||||
|
<ul class="listing">
|
||||||
|
<li><span>{_tour_section_4_text_1|noescape}</span></li>
|
||||||
|
<li><span>{_tour_section_4_text_2|noescape}</span></li>
|
||||||
|
</ul>
|
||||||
|
<img src="assets/packages/static/openvk/img/tour/search.png" width="440">
|
||||||
|
<ul class="listing">
|
||||||
|
<li><span>{_tour_section_4_text_3|noescape}</span></li>
|
||||||
|
</ul>
|
||||||
|
<img src="assets/packages/static/openvk/img/tour/search2.png" width="440">
|
||||||
|
|
||||||
|
<h2>{_tour_section_4_title_2|noescape}</h2>
|
||||||
|
|
||||||
|
<ul class="listing">
|
||||||
|
<li><span>{_tour_section_4_text_4|noescape}</span></li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<img src="assets/packages/static/openvk/img/tour/search_h.png" width="440">
|
||||||
|
|
||||||
|
|
||||||
|
<br>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="videos" class="tabcontent">
|
||||||
|
<h2>{_tour_section_5_title_1|noescape}</h2>
|
||||||
|
|
||||||
|
<ul class="listing">
|
||||||
|
<li><span>{_tour_section_5_text_1|noescape}</span></li>
|
||||||
|
<li><span>{_tour_section_5_text_2|noescape}</span></li>
|
||||||
|
</ul>
|
||||||
|
<img src="assets/packages/static/openvk/img/tour/videos.png" width="440">
|
||||||
|
<p class="big">{_tour_section_5_bottom_text_1|noescape}</p>
|
||||||
|
<img src="assets/packages/static/openvk/img/tour/videos_a.png" width="440">
|
||||||
|
<h2>{_tour_section_5_title_2|noescape}</h2>
|
||||||
|
|
||||||
|
<ul class="listing">
|
||||||
|
<li><span>{_tour_section_5_text_3|noescape}</span></li>
|
||||||
|
</ul>
|
||||||
|
<img src="assets/packages/static/openvk/img/tour/videos_y.png" width="440">
|
||||||
|
|
||||||
|
|
||||||
|
<img src="assets/packages/static/openvk/img/tour/videos_w.png" width="440">
|
||||||
|
|
||||||
|
|
||||||
|
<br>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="audios" class="tabcontent">
|
||||||
|
<h2>{_tour_section_6_title_1|noescape}</h2>
|
||||||
|
|
||||||
|
<ul class="listing">
|
||||||
|
<li><span>{_tour_section_6_text_1|noescape}</span></li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<br>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="news" class="tabcontent">
|
||||||
|
<h2>{_tour_section_7_title_1|noescape}</h2>
|
||||||
|
|
||||||
|
<ul class="listing">
|
||||||
|
<li><span>{_tour_section_7_text_1|noescape}</span></li>
|
||||||
|
<li><span>{_tour_section_7_text_2|noescape}</span></li>
|
||||||
|
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<img src="assets/packages/static/openvk/img/tour/local_news.png" width="440">
|
||||||
|
|
||||||
|
<p class="big">{_tour_section_7_bottom_text_1|noescape}</p>
|
||||||
|
|
||||||
|
|
||||||
|
<br>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="news_global" class="tabcontent">
|
||||||
|
<h2>{_tour_section_8_title_1|noescape}</h2>
|
||||||
|
|
||||||
|
<ul class="listing">
|
||||||
|
<li><span>{_tour_section_8_text_1|noescape}</span></li>
|
||||||
|
<li><span>{_tour_section_8_text_2|noescape}</span></li>
|
||||||
|
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<img src="assets/packages/static/openvk/img/tour/news_global.png" width="440">
|
||||||
|
|
||||||
|
<p class="big">{_tour_section_8_bottom_text_1|noescape}</p>
|
||||||
|
|
||||||
|
|
||||||
|
<img src="assets/packages/static/openvk/img/tour/poll.png" width="440">
|
||||||
|
|
||||||
|
<p class="big">{_tour_section_8_bottom_text_2|noescape}</p>
|
||||||
|
|
||||||
|
|
||||||
|
<br>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="groups" class="tabcontent">
|
||||||
|
<h2>{_tour_section_9_title_1|noescape}</h2>
|
||||||
|
|
||||||
|
<ul class="listing">
|
||||||
|
<li><span>{_tour_section_9_text_1|noescape}</span></li>
|
||||||
|
<li><span>{_tour_section_9_text_2|noescape}</span></li>
|
||||||
|
<li><span>{_tour_section_9_text_3|noescape}</span></li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<img src="assets/packages/static/openvk/img/tour/groups.png" width="440">
|
||||||
|
<p class="big">{_tour_section_9_bottom_text_1|noescape}</p>
|
||||||
|
<img src="assets/packages/static/openvk/img/tour/groups_view.png" width="440">
|
||||||
|
<p class="big">{_tour_section_9_bottom_text_2|noescape}</p>
|
||||||
|
|
||||||
|
<h2>{_tour_section_9_title_2|noescape}</h2>
|
||||||
|
|
||||||
|
<ul class="listing">
|
||||||
|
<li><span>{_tour_section_9_text_2_1|noescape}</span></li>
|
||||||
|
<li><span>{_tour_section_9_text_2_2|noescape}</span></li>
|
||||||
|
<li><span>{_tour_section_9_text_2_3|noescape}</span></li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<img src="assets/packages/static/openvk/img/tour/groups_admin.png" width="440">
|
||||||
|
|
||||||
|
<p class="big">{_tour_section_9_bottom_text_3|noescape}</p>
|
||||||
|
|
||||||
|
|
||||||
|
<br>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="events" class="tabcontent">
|
||||||
|
<h2>{_tour_section_10_title_1|noescape}</h2>
|
||||||
|
|
||||||
|
<ul class="listing">
|
||||||
|
<li><span>{_tour_section_10_text_1|noescape}</span></li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<br>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="themes" class="tabcontent">
|
||||||
|
<h2>{_tour_section_11_title_1|noescape}</h2>
|
||||||
|
|
||||||
|
<ul class="listing">
|
||||||
|
<li><span>{_tour_section_11_text_1|noescape}</span></li>
|
||||||
|
<li><span>{_tour_section_11_text_2|noescape}</span></li>
|
||||||
|
<li><span>{_tour_section_11_text_3|noescape}</span></li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<center><img src="assets/packages/static/openvk/img/tour/theme_picker.png"></center>
|
||||||
|
|
||||||
|
<p class="big">{_tour_section_11_bottom_text_1|noescape}</p><br>
|
||||||
|
|
||||||
|
|
||||||
|
<img src="assets/packages/static/openvk/img/tour/theme3.png" width="460">
|
||||||
|
|
||||||
|
<table cellspacing="5" border="0">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td><img src="assets/packages/static/openvk/img/tour/theme1.png" style="float:left;" width="220"></td>
|
||||||
|
<td><img src="assets/packages/static/openvk/img/tour/theme2.png" style="float:right;" width="220"></td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
|
||||||
|
<br>
|
||||||
|
<center>{_tour_section_11_wordart|noescape}</center>
|
||||||
|
|
||||||
|
|
||||||
|
<br>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="customization" class="tabcontent">
|
||||||
|
<h2>{_tour_section_12_title_1|noescape}</h2>
|
||||||
|
|
||||||
|
<ul class="listing">
|
||||||
|
<li><span>{_tour_section_12_text_1|noescape}</span></li>
|
||||||
|
<li><span>{_tour_section_12_text_2|noescape}</span></li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<img src="assets/packages/static/openvk/img/tour/backdrop.png" width="440">
|
||||||
|
|
||||||
|
<p class="big">{_tour_section_12_bottom_text_1|noescape}</p><br>
|
||||||
|
|
||||||
|
<ul class="listing">
|
||||||
|
<li><span>{_tour_section_12_text_3|noescape}</span></li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
|
<table cellspacing="5" border="0">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td><img src="assets/packages/static/openvk/img/tour/backdrop_ex.png" width="440"></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><img src="assets/packages/static/openvk/img/tour/backdrop_ex1.png" width="440"></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><img src="assets/packages/static/openvk/img/tour/backdrop_ex2.png" width="440"></td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<p class="big">{_tour_section_12_bottom_text_2|noescape}</p><br>
|
||||||
|
|
||||||
|
<p class="big">{_tour_section_12_bottom_text_3|noescape}</p>
|
||||||
|
|
||||||
|
|
||||||
|
<h2>{_tour_section_12_title_2|noescape}</h2>
|
||||||
|
|
||||||
|
<ul class="listing">
|
||||||
|
<li><span>{_tour_section_12_text_2_1|noescape}</span></li>
|
||||||
|
<li><span>{_tour_section_12_text_2_2|noescape}</span></li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<center><img src="assets/packages/static/openvk/img/tour/avatar_picker.png"></center><br>
|
||||||
|
|
||||||
|
<table cellspacing="5" border="0">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td><img src="assets/packages/static/openvk/img/tour/avatars_def.png" style="float:left;" width="220"></td>
|
||||||
|
<td><img src="assets/packages/static/openvk/img/tour/avatars_round.png" style="float:right;" width="220"></td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table><br>
|
||||||
|
|
||||||
|
<img src="assets/packages/static/openvk/img/tour/avatars_quad.png" width="440">
|
||||||
|
|
||||||
|
<h2>{_tour_section_12_title_3|noescape}</h2>
|
||||||
|
|
||||||
|
<ul class="listing">
|
||||||
|
<li><span>{_tour_section_12_text_3_1|noescape}</span></li>
|
||||||
|
<li><span>{_tour_section_12_text_3_2|noescape}</span></li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<table cellspacing="5" border="0">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td><img src="assets/packages/static/openvk/img/tour/leftmenu.png" style="float:left;" width="220"></td>
|
||||||
|
<td><img src="assets/packages/static/openvk/img/tour/leftmenu2.png" style="float:right;" width="220"></td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<h2>{_tour_section_12_title_4|noescape}</h2>
|
||||||
|
<ul class="listing">
|
||||||
|
<li><span>{_tour_section_12_text_4_1|noescape}</span></li>
|
||||||
|
<li><span>{_tour_section_12_text_4_2|noescape}</span></li>
|
||||||
|
<li><span>{_tour_section_12_text_4_3|noescape}</span></li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<center><img src="assets/packages/static/openvk/img/tour/wall_pick.png"></center><br>
|
||||||
|
|
||||||
|
<table cellspacing="5" border="0">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td><img src="assets/packages/static/openvk/img/tour/wall_old.png" style="float:left;" width="220">
|
||||||
|
<br> <p class="big"{_tour_section_12_bottom_text_4|noescape}</p></td>
|
||||||
|
<td><img src="assets/packages/static/openvk/img/tour/wall_new.png" style="float:right;" width="220">
|
||||||
|
<br><p class="big">{_tour_section_12_bottom_text_5|noescape}</p></td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<br>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="vouchers" class="tabcontent">
|
||||||
|
<h2>{_tour_section_13_title_1|noescape}</h2>
|
||||||
|
|
||||||
|
<ul class="listing">
|
||||||
|
<li><span>{_tour_section_13_text_1|noescape}</span></li>
|
||||||
|
<li><span>{_tour_section_13_text_2|noescape}</span></li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<img src="assets/packages/static/openvk/img/tour/vouchers.png" width="440">
|
||||||
|
|
||||||
|
<img src="assets/packages/static/openvk/img/tour/vouchers_type.png" width="440">
|
||||||
|
<p class="big">{_tour_section_13_bottom_text_1|noescape}</p><br>
|
||||||
|
|
||||||
|
<ul class="listing">
|
||||||
|
<li><span>{_tour_section_13_text_3|noescape}</span></li>
|
||||||
|
<li><span>{_tour_section_13_text_4|noescape}</span></li>
|
||||||
|
</ul>
|
||||||
|
<img src="assets/packages/static/openvk/img/tour/vouchers_ok.png" width="440">
|
||||||
|
|
||||||
|
<p class="big">{_tour_section_13_bottom_text_2|noescape}</p><br>
|
||||||
|
|
||||||
|
<p class="big">{_tour_section_13_bottom_text_3|noescape}</p><br>
|
||||||
|
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<br>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="mobile" class="tabcontent">
|
||||||
|
<h2>{_tour_section_14_title_1|noescape}</h2>
|
||||||
|
|
||||||
|
<ul class="listing">
|
||||||
|
<li><span>{_tour_section_14_text_1|noescape}</span></li>
|
||||||
|
<li><span>{_tour_section_14_text_2|noescape}</span></li>
|
||||||
|
<li><span>{_tour_section_14_text_3|noescape}</span></li>
|
||||||
|
</ul>
|
||||||
|
<table cellspacing="5" border="0">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td><img src="assets/packages/static/openvk/img/tour/app1.png" style="float:left;" width="210"></td>
|
||||||
|
<td><img src="assets/packages/static/openvk/img/tour/app2.png" style="float:right;" width="210"></td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
|
||||||
|
<table cellspacing="5" border="0">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td><img src="assets/packages/static/openvk/img/tour/app3.png" width="425"></td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<p class="big">{_tour_section_14_bottom_text_1|noescape}</p><br>
|
||||||
|
|
||||||
|
<h2>{_tour_section_14_title_2|noescape}</h2>
|
||||||
|
|
||||||
|
<ul class="listing">
|
||||||
|
<li><span>{_tour_section_14_text_2_1|noescape}</span></li>
|
||||||
|
<li><span>{_tour_section_14_text_2_2|noescape}</span></li>
|
||||||
|
<li><span>{_tour_section_14_text_2_3|noescape}</span></li>
|
||||||
|
</ul>
|
||||||
|
<img src="assets/packages/static/openvk/img/tour/app4.jpeg" width="440">
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
|
<p class="big" n:if="!isset($thisUser)">{_tour_section_14_bottom_text_2|noescape}</p><br>
|
||||||
|
<p class="big" n:if="isset($thisUser)">{_tour_section_14_bottom_text_3|noescape}</p><br>
|
||||||
|
|
||||||
|
<div style="margin-top:10px; padding-left:175px" n:if="!isset($thisUser)">
|
||||||
|
|
||||||
|
<a class="button" href="/reg">{_tour_reg|noescape}</a>
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<br>
|
||||||
|
</div>
|
||||||
|
<!--
|
||||||
|
.__ .__ __ .__ .___.__ __
|
||||||
|
| |__ ____ |__|/ |_ ___________ ___.__. |__| __| _/|__|/ |_ ____ ___ __ _____ ____ _____ ____ _____ ______
|
||||||
|
| | \_/ __ \| \ __\/ __ \_ __ < | | | |/ __ | | \ __\/ __ \ \ \/ / \__ \ / \\__ \ / \\__ \ / ___/
|
||||||
|
| Y \ ___/| || | \ ___/| | \/\___ | | / /_/ | | || | \ ___/ \ / / __ \| | \/ __ \| | \/ __ \_\___ \
|
||||||
|
|___| /\___ >__||__| \___ >__| / ____| |__\____ | |__||__| \___ > \_/ (____ /___| (____ /___| (____ /____ >
|
||||||
|
\/ \/ \/ \/ \/ \/ \/ \/ \/ \/ \/ \/
|
||||||
|
.__ .__ __ __
|
||||||
|
_____ ___.__. _____| | |__|__ __ _____/ |_ ______ _____ ___ ___/ |_ ___________
|
||||||
|
/ < | |/ ___/ | | \ \/ // __ \ __\/ ___/ \__ \\ \/ /\ __\/ _ \_ __ \
|
||||||
|
| Y Y \___ |\___ \| |_| |\ /\ ___/| | \___ \ / __ \\ / | | ( <_> ) | \/
|
||||||
|
|__|_| / ____/____ >____/__| \_/ \___ >__| /____ > (____ /\_/ |__| \____/|__|
|
||||||
|
\/\/ \/ \/ \/ \/ -->
|
||||||
|
{script "js/tour.js"}
|
||||||
|
{/block}
|
|
@ -411,7 +411,7 @@
|
||||||
<tr>
|
<tr>
|
||||||
<td class="e">
|
<td class="e">
|
||||||
Vladimir Barinov (veselcraft) and Konstantin Kichulkin (kosfurler)<br/>
|
Vladimir Barinov (veselcraft) and Konstantin Kichulkin (kosfurler)<br/>
|
||||||
OpenVK is a free open-source software that "cosplays" (or imitates) older versions of russian website VKontakte. VKontakte belongs to Pavel Durov and VK Group.
|
OpenVK is a free open source software that "cosplays" (or imitates) older versions of a Russian social network called VKontakte. VKontakte belongs to Pavel Durov and VK Group.
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
|
@ -430,7 +430,7 @@
|
||||||
Native name
|
Native name
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
Author
|
Author(s)
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{foreach $languages as $language}
|
{foreach $languages as $language}
|
||||||
|
@ -458,7 +458,7 @@
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td class="e">Initial hosting</td>
|
<td class="e">Initial hosting</td>
|
||||||
<td class="v">Ilya Prokopenko (dsrev) and Celestora</td>
|
<td class="v">Lumaeris and Celestora</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td class="e">Initial bug-tracker hosting</td>
|
<td class="e">Initial bug-tracker hosting</td>
|
||||||
|
@ -492,7 +492,7 @@
|
||||||
<td>
|
<td>
|
||||||
kovaltim, Vladimir Lapskiy (0x7d5), Alexander Minkin (WerySkok), Polina Katunina (RousPhaul), veth,
|
kovaltim, Vladimir Lapskiy (0x7d5), Alexander Minkin (WerySkok), Polina Katunina (RousPhaul), veth,
|
||||||
Egor Shevchenko, Vadim Korovin (yuni), Ash Defenders,
|
Egor Shevchenko, Vadim Korovin (yuni), Ash Defenders,
|
||||||
Pavel Silaev, Dmitriy Daemon, Ilya Prokopenko (dsrev),
|
Pavel Silaev, Dmitriy Daemon, Lumaeris,
|
||||||
cmed404 and unknown tester, who disappeared shortly after trying to upload post with cat.
|
cmed404 and unknown tester, who disappeared shortly after trying to upload post with cat.
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
<style>
|
<style>
|
||||||
{var $css = file_get_contents(OPENVK_ROOT . "/Web/static/js/node_modules/@atlassian/aui/dist/aui/aui-prototyping.css")}
|
{var $css = file_get_contents(OPENVK_ROOT . "/Web/static/js/node_modules/@atlassian/aui/dist/aui/aui-prototyping.css")}
|
||||||
{str_replace("fonts/", "/assets/packages/static/openvk/js/node_modules/@atlassian/aui/dist/aui/fonts/", $css)|noescape}
|
{str_replace("fonts/", "/assets/packages/static/openvk/js/node_modules/@atlassian/aui/dist/aui/fonts/", $css)|noescape}
|
||||||
|
{file_get_contents(OPENVK_ROOT . "/Web/static/js/node_modules/@atlassian/aui/dist/aui/aui-prototyping-darkmode.css")|noescape}
|
||||||
</style>
|
</style>
|
||||||
<title>{include title} - {_admin} {$instance_name}</title>
|
<title>{include title} - {_admin} {$instance_name}</title>
|
||||||
</head>
|
</head>
|
||||||
|
@ -21,12 +22,49 @@
|
||||||
</a>
|
</a>
|
||||||
</h1>
|
</h1>
|
||||||
</div>
|
</div>
|
||||||
<div n:if="$search ?? false" class="aui-header-secondary">
|
<div class="aui-header-secondary">
|
||||||
<ul class="aui-nav">
|
<ul class="aui-nav">
|
||||||
<form class="aui-quicksearch dont-default-focus ajs-dirty-warning-exempt">
|
<li n:if="$search ?? false">
|
||||||
<input id="quickSearchInput" autocomplete="off" class="search" type="text" placeholder="{include searchTitle}" value="{$_GET['q'] ?? ''}" name="q" accesskey="Q" />
|
<form class="aui-quicksearch dont-default-focus ajs-dirty-warning-exempt">
|
||||||
<input type="hidden" value=1 name=p />
|
<input id="quickSearchInput" autocomplete="off" class="search" type="text" placeholder="{include searchTitle}" value="{$_GET['q'] ?? ''}" name="q" accesskey="Q" />
|
||||||
</form>
|
<input type="hidden" value=1 name=p />
|
||||||
|
</form>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<aui-toggle id="switch-theme" label="Toggle dark mode"></aui-toggle>
|
||||||
|
<script>
|
||||||
|
const toggle = document.getElementById("switch-theme");
|
||||||
|
let currentTheme = localStorage.getItem("ovkadmin-theme");
|
||||||
|
|
||||||
|
if (currentTheme == null) {
|
||||||
|
const preferDarkScheme = window.matchMedia("(prefers-color-scheme: dark)");
|
||||||
|
let theme = "light";
|
||||||
|
|
||||||
|
if (preferDarkScheme.matches) {
|
||||||
|
theme = "dark";
|
||||||
|
document.body.classList.add("aui-theme-dark");
|
||||||
|
}
|
||||||
|
|
||||||
|
localStorage.setItem("ovkadmin-theme", theme);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentTheme == "dark") {
|
||||||
|
document.body.classList.add("aui-theme-dark");
|
||||||
|
}
|
||||||
|
|
||||||
|
toggle.addEventListener("click", function() {
|
||||||
|
document.body.classList.toggle("aui-theme-dark");
|
||||||
|
|
||||||
|
let theme = "light";
|
||||||
|
|
||||||
|
if (document.body.classList.contains("aui-theme-dark")) {
|
||||||
|
theme = "dark";
|
||||||
|
}
|
||||||
|
|
||||||
|
localStorage.setItem("ovkadmin-theme", theme);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -34,7 +72,7 @@
|
||||||
</header>
|
</header>
|
||||||
<div class="aui-page-panel">
|
<div class="aui-page-panel">
|
||||||
<div class="aui-page-panel-inner">
|
<div class="aui-page-panel-inner">
|
||||||
<div class="aui-page-panel-nav" style="background-color: #fff;">
|
<div class="aui-page-panel-nav">
|
||||||
<nav class="aui-navgroup aui-navgroup-vertical">
|
<nav class="aui-navgroup aui-navgroup-vertical">
|
||||||
<div class="aui-navgroup-inner">
|
<div class="aui-navgroup-inner">
|
||||||
<div class="aui-navgroup-primary">
|
<div class="aui-navgroup-primary">
|
||||||
|
|
|
@ -69,6 +69,16 @@
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<hr/>
|
<hr/>
|
||||||
|
<div class="field-group">
|
||||||
|
<label for="email">{_password}</label>
|
||||||
|
<input class="text medium-field" type="password" id="password" name="password" value="" />
|
||||||
|
</div>
|
||||||
|
<div class="buttons-container">
|
||||||
|
<div class="buttons">
|
||||||
|
<a class="button" onclick="let pswd = Math.random().toString(27).slice(2,12); alert('Сгенерированный пароль: ' + pswd + '\n\nНе забудьте сообщить пользователю, чтобы он сменил пароль на свой!'); $('input#password').val(pswd);">Сгенерировать пароль</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr/>
|
||||||
<h2>{_c_groups}</h2>
|
<h2>{_c_groups}</h2>
|
||||||
<div>
|
<div>
|
||||||
<div class="field-group">
|
<div class="field-group">
|
||||||
|
|
|
@ -9,21 +9,47 @@
|
||||||
{/block}
|
{/block}
|
||||||
|
|
||||||
{block content}
|
{block content}
|
||||||
<p>
|
<h4 style="margin-left: 100px; margin-right: 100px;">{_access_recovery}</h4>
|
||||||
{_access_recovery_info_2}
|
<table cellspacing="10" cellpadding="0" border="0" align="center" width="70%">
|
||||||
</p>
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
{_access_recovery_info_2}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
<form method="POST" enctype="multipart/form-data">
|
<form method="POST" enctype="multipart/form-data">
|
||||||
<label for="password">{_new_password}: </label>
|
<table cellspacing="7" cellpadding="0" width="55%" border="0" align="center">
|
||||||
<input id="password" type="password" name="password" required />
|
<tbody>
|
||||||
<br/><br/>
|
<tr>
|
||||||
{if $is2faEnabled}
|
<td class="regform-left">
|
||||||
<label for="code">{_"2fa_code_2"}: </label>
|
<span class="nobold">{_new_password}: </span>
|
||||||
<input id="code" type="text" name="code" required />
|
</td>
|
||||||
<br/><br/>
|
<td class="regform-right">
|
||||||
{/if}
|
<input id="password" type="password" name="password" required />
|
||||||
|
</td>
|
||||||
<input type="hidden" name="hash" value="{$csrfToken}" />
|
</tr>
|
||||||
<input type="submit" value="{_reset_password}" class="button" style="float: right;" />
|
{if $is2faEnabled}
|
||||||
|
<tr style="text-align: right;">
|
||||||
|
<td class="regform-left">
|
||||||
|
<span class="nobold">{_"2fa_code_2"}: </span>
|
||||||
|
</td>
|
||||||
|
<td class="regform-right">
|
||||||
|
<input id="password" type="password" name="password" required />
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{/if}
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<center>
|
||||||
|
<input type="hidden" name="hash" value="{$csrfToken}" />
|
||||||
|
<input type="submit" value="{_reset_password}" class="button" />
|
||||||
|
</center>
|
||||||
</form>
|
</form>
|
||||||
{/block}
|
{/block}
|
||||||
|
|
|
@ -7,35 +7,36 @@
|
||||||
|
|
||||||
{block content}
|
{block content}
|
||||||
<form method="POST" enctype="multipart/form-data">
|
<form method="POST" enctype="multipart/form-data">
|
||||||
<table cellspacing="7" cellpadding="0" width="40%" border="0" align="center">
|
<h4 style="margin-left: 100px; margin-right: 100px;">{_log_in}</h4>
|
||||||
|
<table cellspacing="7" cellpadding="0" width="46%" border="0" align="center">
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr style="text-align: right;">
|
||||||
<td>
|
<td>
|
||||||
<span>{_email}: </span>
|
<span class="nobold">{_email}: </span>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td style="width:191px;">
|
||||||
<input type="text" name="login" required />
|
<input type="text" name="login" required />
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr style="text-align: right;">
|
||||||
<td>
|
<td>
|
||||||
<span>{_password}: </span>
|
<span class="nobold">{_password}: </span>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<input type="password" name="password" required />
|
<input type="password" name="password" required />
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<input type="hidden" name="hash" value="{$csrfToken}" />
|
|
||||||
<input type="submit" value="{_log_in}" class="button" />
|
|
||||||
<a href="/reg">{_registration}</a>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
<center>
|
||||||
|
<!-- div style="margin-bottom: 8px;">
|
||||||
|
<input type="checkbox" name="someone_pc" value=""/>
|
||||||
|
<label for="someone_pc" class="nobold">{_not_your_pc}?</label><br>
|
||||||
|
</div -->
|
||||||
|
<input type="hidden" name="hash" value="{$csrfToken}" />
|
||||||
|
<input type="submit" value="{_log_in}" class="button" />
|
||||||
|
<a href="/reg" class="button" style="display: inline-block;">{_registration}</a><br><br>
|
||||||
|
<a href="/restore">{_forgot_password}</a>
|
||||||
|
</center>
|
||||||
</form>
|
</form>
|
||||||
{/block}
|
{/block}
|
||||||
|
|
|
@ -6,33 +6,40 @@
|
||||||
{/block}
|
{/block}
|
||||||
|
|
||||||
{block content}
|
{block content}
|
||||||
<p>
|
<h4 style="margin-left: 100px; margin-right: 100px;">{_two_factor_authentication}</h4>
|
||||||
{_two_factor_authentication_login}
|
<table cellspacing="10" cellpadding="0" border="0" align="center" width="70%">
|
||||||
</p>
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
{_two_factor_authentication_login}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
<form method="POST" enctype="multipart/form-data">
|
<form method="POST" enctype="multipart/form-data">
|
||||||
<table cellspacing="7" cellpadding="0" width="40%" border="0" align="center">
|
<table cellspacing="7" cellpadding="0" width="40%" border="0" align="center">
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr style="text-align: right;">
|
||||||
<td>
|
<td>
|
||||||
<span>{_code}: </span>
|
<span class="nobold">{_code}: </span>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<input type="text" name="code" autocomplete="off" required />
|
<input type="text" name="code" autocomplete="off" required autofocus />
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
|
|
||||||
</td>
|
</td>
|
||||||
<td>
|
|
||||||
<input type="hidden" name="login" value="{$login}" />
|
|
||||||
<input type="hidden" name="password" value="{$password}" />
|
|
||||||
<input type="hidden" name="hash" value="{$csrfToken}" />
|
|
||||||
<input type="submit" value="{_log_in}" class="button" />
|
|
||||||
</td>
|
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
<center>
|
||||||
|
<input type="hidden" name="login" value="{$login}" />
|
||||||
|
<input type="hidden" name="password" value="{$password}" />
|
||||||
|
<input type="hidden" name="hash" value="{$csrfToken}" />
|
||||||
|
<input type="submit" value="{_log_in}" class="button" />
|
||||||
|
</center>
|
||||||
</form>
|
</form>
|
||||||
{/block}
|
{/block}
|
||||||
|
|
|
@ -22,31 +22,78 @@
|
||||||
<p n:if="!is_null($referer)" align="center">
|
<p n:if="!is_null($referer)" align="center">
|
||||||
{tr("invites_you_to", $referer->getFullName(), OPENVK_ROOT_CONF['openvk']['appearance']['name'])|noescape}
|
{tr("invites_you_to", $referer->getFullName(), OPENVK_ROOT_CONF['openvk']['appearance']['name'])|noescape}
|
||||||
</p>
|
</p>
|
||||||
|
<div style="margin: 10px;">
|
||||||
|
<h2 class="header2">{_registration}</h2>
|
||||||
|
<table cellspacing="10" cellpadding="0" border="0" align="center" style="margin: 9px;">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<img src="assets/packages/static/openvk/img/favicons/favicon64.png" style="width: 32px;" align="middle">
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<b>{php echo OPENVK_ROOT_CONF['openvk']['appearance']['name']} {_registration_welcome_1}</b><br>
|
||||||
|
{_registration_welcome_2}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
<form method="POST" enctype="multipart/form-data">
|
<form method="POST" enctype="multipart/form-data">
|
||||||
<table cellspacing="7" cellpadding="0" width="52%" border="0" align="center">
|
<table cellspacing="7" cellpadding="0" width="52%" border="0" align="center">
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<h4 style="margin-left: 60px;">{_main}</h4>
|
||||||
<span>{_name}: </span>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="regform-left">
|
||||||
|
<span class="nobold">{_name}: </span>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td class="regform-right">
|
||||||
<input type="text" name="first_name" required />
|
<input type="text" name="first_name" required />
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td class="regform-left">
|
||||||
<span>{_surname}: </span>
|
<span class="nobold">{_surname}: </span>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td class="regform-right">
|
||||||
<input type="text" name="last_name" />
|
<input type="text" name="last_name" />
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td class="regform-left">
|
||||||
<span>{_gender}: </span>
|
<span class="nobold">{_email}: </span>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td class="regform-right">
|
||||||
|
<input type="email" name="email" required />
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="regform-left">
|
||||||
|
<span class="nobold">{_password}: </span>
|
||||||
|
</td>
|
||||||
|
<td class="regform-right">
|
||||||
|
<input type="password" name="password" required />
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
<table cellspacing="7" cellpadding="0" width="52%" border="0" align="center">
|
||||||
|
<tr>
|
||||||
|
<h4 style="margin-left: 60px;">{_other_fields}</h4>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="regform-left">
|
||||||
|
<span class="nobold">{_birth_date}: </span>
|
||||||
|
</td>
|
||||||
|
<td class="regform-right">
|
||||||
|
<input max={date('Y-m-d')} name="birthday" type="date"/>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="regform-left">
|
||||||
|
<span class="nobold">{_gender}: </span>
|
||||||
|
</td>
|
||||||
|
<td class="regform-right">
|
||||||
{var $femalePreferred = OPENVK_ROOT_CONF["openvk"]["preferences"]["femaleGenderPriority"]}
|
{var $femalePreferred = OPENVK_ROOT_CONF["openvk"]["preferences"]["femaleGenderPriority"]}
|
||||||
<select name="sex" required>
|
<select name="sex" required>
|
||||||
<option n:attr="selected => !$femalePreferred" value="male">{_male}</option>
|
<option n:attr="selected => !$femalePreferred" value="male">{_male}</option>
|
||||||
|
@ -54,64 +101,43 @@
|
||||||
</select>
|
</select>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
{if !(strpos(captcha_template(), 'verified'))}
|
||||||
<td>
|
<tr>
|
||||||
<span>{_birth_date}: </span>
|
<td class="regform-left">
|
||||||
</td>
|
<span class="nobold">CAPTCHA: </span>
|
||||||
<td>
|
</td>
|
||||||
<input max={date('Y-m-d')} name="birthday" type="date"/>
|
<td class="regform-right">
|
||||||
</td>
|
{captcha_template()|noescape}
|
||||||
</tr>
|
</td>
|
||||||
<tr></tr>
|
</tr>
|
||||||
<tr>
|
{/if}
|
||||||
<td>
|
</table>
|
||||||
<span>{_email}: </span>
|
<div style="margin-left: 100px; margin-right: 100px; text-align: center;">
|
||||||
</td>
|
<input type="hidden" name="hash" value="{$csrfToken}" />
|
||||||
<td>
|
<input type="checkbox" required="true" name="confirmation" /> {_checkbox_in_registration|noescape}
|
||||||
<input type="email" name="email" required />
|
<br /><br />
|
||||||
</td>
|
<input type="submit" value="{_registration}" class="button" /><br><br>
|
||||||
</tr>
|
</div>
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<span>{_password}: </span>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<input type="password" name="password" required />
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<span>CAPTCHA: </span>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
{captcha_template()|noescape}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<input type="hidden" name="hash" value="{$csrfToken}" />
|
|
||||||
<input type="checkbox" required="true" name="confirmation" /> {_checkbox_in_registration|noescape}
|
|
||||||
<br /><br />
|
|
||||||
<input type="submit" value="{_registration}" class="button" />
|
|
||||||
<a href="/login">{_log_in}</a>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
|
||||||
</form>
|
</form>
|
||||||
|
</div>
|
||||||
{else}
|
{else}
|
||||||
<center>
|
<h4>{_registration_closed}</h4>
|
||||||
<img src="/assets/packages/static/openvk/img/oof.apng" alt="{_registration_closed}" style="width: 20%;" />
|
<table cellspacing="10" cellpadding="0" border="0" align="center" style="margin: 9px;">
|
||||||
<p>
|
<tbody>
|
||||||
{_registration_disabled_info}
|
<tr>
|
||||||
{if OPENVK_ROOT_CONF['openvk']['preferences']['registration']['reason']}
|
<td style="width: 20%;">
|
||||||
<br/><br/><b>{php echo OPENVK_ROOT_CONF['openvk']['preferences']['registration']['reason']}</b>
|
<img src="/assets/packages/static/openvk/img/oof.apng" alt="{_registration_closed}" style="width: 100%;"/>
|
||||||
{/if}
|
</td>
|
||||||
</p>
|
<td>
|
||||||
</center>
|
{_registration_disabled_info}
|
||||||
|
{if OPENVK_ROOT_CONF['openvk']['preferences']['registration']['disablingReason']}
|
||||||
|
<br/><br/>{_admin_banned_link_reason}:<br>
|
||||||
|
<b>{php echo OPENVK_ROOT_CONF['openvk']['preferences']['registration']['disablingReason']}</b>
|
||||||
|
{/if}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
{/if}
|
{/if}
|
||||||
{/block}
|
{/block}
|
||||||
|
|
|
@ -9,16 +9,37 @@
|
||||||
{/block}
|
{/block}
|
||||||
|
|
||||||
{block content}
|
{block content}
|
||||||
<p>
|
<h4 style="margin-left: 100px; margin-right: 100px;">{_access_recovery}</h4>
|
||||||
{_access_recovery_info}
|
<table cellspacing="10" cellpadding="0" border="0" align="center" width="70%">
|
||||||
</p>
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
{_access_recovery_info}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
<form method="POST" enctype="multipart/form-data">
|
<form method="POST" enctype="multipart/form-data">
|
||||||
<label for="login">{_log_in}: </label>
|
<table cellspacing="7" cellpadding="0" width="46%" border="0" align="center">
|
||||||
<input id="login" type="text" name="login" required />
|
<tbody>
|
||||||
<br/><br/>
|
<tr style="text-align: right;">
|
||||||
|
<td>
|
||||||
<input type="hidden" name="hash" value="{$csrfToken}" />
|
<span class="nobold">{_email}: </span>
|
||||||
<input type="submit" value="{_reset_password}" class="button" style="float: right;" />
|
</td>
|
||||||
|
<td>
|
||||||
|
<input id="login" type="text" name="login" required />
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<center>
|
||||||
|
<input type="hidden" name="hash" value="{$csrfToken}" />
|
||||||
|
<input type="submit" value="{_reset_password}" class="button"/>
|
||||||
|
</center>
|
||||||
</form>
|
</form>
|
||||||
{/block}
|
{/block}
|
||||||
|
|
|
@ -13,13 +13,13 @@
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="tab">
|
<div class="tab">
|
||||||
<a href="/club{$club->getId()}/followers">
|
<a href="/club{$club->getId()}/backdrop">
|
||||||
{_followers}
|
{_backdrop_short}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="tab">
|
<div class="tab">
|
||||||
<a href="/club{$club->getId()}/stats">
|
<a href="/club{$club->getId()}/followers">
|
||||||
{_statistics}
|
{_followers}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -66,7 +66,10 @@
|
||||||
<span class="nobold">{_avatar}: </span>
|
<span class="nobold">{_avatar}: </span>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<input type="file" name="ava" accept="image/*" />
|
<label class="button" style="">{_browse}
|
||||||
|
<input type="file" id="ava" name="ava" style="display: none;" onchange="filename.innerHTML=ava.files[0].name" />
|
||||||
|
</label>
|
||||||
|
<div id="filename" style="margin-top: 10px;"></div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
|
|
61
Web/Presenters/templates/Group/EditBackdrop.xml
Normal file
61
Web/Presenters/templates/Group/EditBackdrop.xml
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
{extends "../@layout.xml"}
|
||||||
|
{var $backdrops = $club->getBackDropPictureURLs()}
|
||||||
|
|
||||||
|
{block title}{$club->getName()} | {_backdrop}{/block}
|
||||||
|
|
||||||
|
{block header}
|
||||||
|
<a href="{$club->getURL()}">{$club->getName()}</a> » {_backdrop}
|
||||||
|
{/block}
|
||||||
|
|
||||||
|
{block content}
|
||||||
|
<div class="tabs">
|
||||||
|
<div class="tab">
|
||||||
|
<a href="/club{$club->getId()}/edit">
|
||||||
|
{_main}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div id="activetabs" class="tab">
|
||||||
|
<a id="act_tab_a" href="/club{$club->getId()}/backdrop">
|
||||||
|
{_backdrop_short}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="tab">
|
||||||
|
<a href="/club{$club->getId()}/followers">
|
||||||
|
{_followers}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="container_gray">
|
||||||
|
<h4>{_backdrop}</h4>
|
||||||
|
<p>{_backdrop_desc}</p>
|
||||||
|
<form method="POST" enctype="multipart/form-data">
|
||||||
|
<div id="backdropEditor">
|
||||||
|
<div id="backdropFilePicker">
|
||||||
|
<label class="button" style="">{_browse}
|
||||||
|
<input type="file" accept="image/*" name="backdrop1" style="display: none;">
|
||||||
|
</label>
|
||||||
|
<div id="spacer" style="width: 366px;"></div>
|
||||||
|
<label class="button" style="">{_browse}<input type="file" accept="image/*" name="backdrop2" style="display: none;"></label>
|
||||||
|
<div id="spacer" style="width: 366px;"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<span class="nobold">{_backdrop_warn}</span>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<span class="nobold">{_backdrop_about_adding}</span>
|
||||||
|
</p>
|
||||||
|
<p><br/></p>
|
||||||
|
|
||||||
|
<input type="hidden" name="hash" value="{$csrfToken}" />
|
||||||
|
<div>
|
||||||
|
<center>
|
||||||
|
<button name="subact" value="save" class="button">{_backdrop_save}</button>
|
||||||
|
<button name="subact" value="remove" class="button">{_backdrop_remove}</button>
|
||||||
|
</center>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
{/block}
|
|
@ -23,16 +23,16 @@
|
||||||
{_main}
|
{_main}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="tab">
|
||||||
|
<a href="/club{$club->getId()}/backdrop">
|
||||||
|
{_backdrop_short}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
<div id="activetabs" class="tab">
|
<div id="activetabs" class="tab">
|
||||||
<a id="act_tab_a" href="/club{$club->getId()}/followers">
|
<a id="act_tab_a" href="/club{$club->getId()}/followers">
|
||||||
{_followers}
|
{_followers}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="tab">
|
|
||||||
<a href="/club{$club->getId()}/stats">
|
|
||||||
{_statistics}
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
{/if}
|
{/if}
|
||||||
{/block}
|
{/block}
|
||||||
|
|
||||||
|
|
|
@ -6,25 +6,7 @@
|
||||||
{/block}
|
{/block}
|
||||||
|
|
||||||
{block content}
|
{block content}
|
||||||
<div class="tabs">
|
<div>
|
||||||
<div class="tab">
|
|
||||||
<a href="/club{$club->getId()}/edit">
|
|
||||||
{_main}
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
<div class="tab">
|
|
||||||
<a href="/club{$club->getId()}/followers">
|
|
||||||
{_followers}
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
<div id="activetabs" class="tab">
|
|
||||||
<a id="act_tab_a" href="javascript:void(0)">
|
|
||||||
{_statistics}
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="container_gray">
|
|
||||||
<h4>Охват</h4>
|
<h4>Охват</h4>
|
||||||
<p>Этот график отображает охват за последние 7 дней.</p>
|
<p>Этот график отображает охват за последние 7 дней.</p>
|
||||||
<div id="reachChart" style="width: 100%; height: 280px;"></div>
|
<div id="reachChart" style="width: 100%; height: 280px;"></div>
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
{extends "../@layout.xml"}
|
{extends "../@layout.xml"}
|
||||||
|
{var $backdrops = $club->getBackDropPictureURLs()}
|
||||||
|
|
||||||
{block title}{$club->getName()}{/block}
|
{block title}{$club->getName()}{/block}
|
||||||
|
|
||||||
|
@ -95,12 +96,31 @@
|
||||||
<div class="right_small_block">
|
<div class="right_small_block">
|
||||||
{var $avatarPhoto = $club->getAvatarPhoto()}
|
{var $avatarPhoto = $club->getAvatarPhoto()}
|
||||||
{var $avatarLink = ((is_null($avatarPhoto) ? FALSE : $avatarPhoto->isAnonymous()) ? "/photo" . ("s/" . base_convert((string) $avatarPhoto->getId(), 10, 32)) : $club->getAvatarLink())}
|
{var $avatarLink = ((is_null($avatarPhoto) ? FALSE : $avatarPhoto->isAnonymous()) ? "/photo" . ("s/" . base_convert((string) $avatarPhoto->getId(), 10, 32)) : $club->getAvatarLink())}
|
||||||
<a href="{$avatarLink|nocheck}">
|
<div class="avatar_block" style="position:relative;">
|
||||||
<img src="{$club->getAvatarUrl('normal')}" style="width: 100%; image-rendering: -webkit-optimize-contrast;" />
|
{var $hasAvatar = !str_contains($club->getAvatarUrl('miniscule'), "/assets/packages/static/openvk/img/camera_200.png")}
|
||||||
</a>
|
{if !is_null($thisUser) && $hasAvatar == false && $club->canBeModifiedBy($thisUser)}
|
||||||
|
<a href="javascript:addAvatarImage(true, {$club->getId()})" class="text_add_image">{_add_image_group}</a>
|
||||||
|
{elseif !is_null($thisUser) && $hasAvatar == true && $club->canBeModifiedBy($thisUser)}
|
||||||
|
<div class="avatar_controls">
|
||||||
|
<div class="avatarDelete">
|
||||||
|
<a id="upl" href="javascript:deleteAvatar('{$club->getAvatarPhoto()->getPrettyId()}')"><img src="/assets/packages/static/openvk/img/delete.png"/></a>
|
||||||
|
</div>
|
||||||
|
<div class="avatar_variants">
|
||||||
|
<div class="variant">
|
||||||
|
<img src="/assets/packages/static/openvk/img/upload.png" style="margin-left:15px;height: 10px;">
|
||||||
|
<a href="javascript:addAvatarImage(true, {$club->getId()})"><p>{_upload_new_picture}</p></a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
<a href="{$avatarLink|nocheck}">
|
||||||
|
<img src="{$club->getAvatarUrl('normal')}" id="thisGroupAvatar" style="width: 100%; image-rendering: -webkit-optimize-contrast;" />
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
<div n:ifset="$thisUser" id="profile_links">
|
<div n:ifset="$thisUser" id="profile_links">
|
||||||
{if $club->canBeModifiedBy($thisUser)}
|
{if $club->canBeModifiedBy($thisUser)}
|
||||||
<a href="/club{$club->getId()}/edit" id="profile_link">{_edit_group}</a>
|
<a href="/club{$club->getId()}/edit" id="profile_link">{_edit_group}</a>
|
||||||
|
<a href="/club{$club->getId()}/stats" id="profile_link">{_statistics}</a>
|
||||||
{/if}
|
{/if}
|
||||||
{if $thisUser->getChandlerUser()->can("access")->model("admin")->whichBelongsTo(NULL)}
|
{if $thisUser->getChandlerUser()->can("access")->model("admin")->whichBelongsTo(NULL)}
|
||||||
<a href="/admin/clubs/id{$club->getId()}" id="profile_link">{_manage_group_action}</a>
|
<a href="/admin/clubs/id{$club->getId()}" id="profile_link">{_manage_group_action}</a>
|
||||||
|
@ -241,3 +261,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/block}
|
{/block}
|
||||||
|
|
||||||
|
{block bodyScripts}
|
||||||
|
{script "js/al_despacito_wall.js"}
|
||||||
|
{/block}
|
|
@ -47,7 +47,7 @@
|
||||||
window._preview = undefined;
|
window._preview = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
window._preview = window.open("data:text/html,", "_blank", { popup: true });
|
window._preview = window.open("about:blank", "_blank", { popup: true });
|
||||||
window._preview.document.write(`<style>form { display: none; }</style><form action="${ location.origin }/notes/prerender" method="POST" enctype="multipart/form-data"><input name="title" /><input name="html" /><input name="hash" /></form>`);
|
window._preview.document.write(`<style>form { display: none; }</style><form action="${ location.origin }/notes/prerender" method="POST" enctype="multipart/form-data"><input name="title" /><input name="html" /><input name="hash" /></form>`);
|
||||||
window._preview.document.querySelector("input[name=title]").value = document.querySelector("input[name=name]").value;
|
window._preview.document.querySelector("input[name=title]").value = document.querySelector("input[name=name]").value;
|
||||||
window._preview.document.querySelector("input[name=html]").value = window._editor.getValue();
|
window._preview.document.querySelector("input[name=html]").value = window._editor.getValue();
|
||||||
|
|
|
@ -24,9 +24,14 @@
|
||||||
<table class="post post-divider" border="0" style="font-size: 11px;" n:foreach="$data as $dat">
|
<table class="post post-divider" border="0" style="font-size: 11px;" n:foreach="$data as $dat">
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
|
{var $sxModel = $dat->getModel(1)}
|
||||||
|
{if !(method_exists($sxModel, "getURL") && method_exists($sxModel, "getAvatarUrl"))}
|
||||||
|
{var $sxModel = $dat->getModel(0)}
|
||||||
|
{/if}
|
||||||
|
|
||||||
<td width="54" valign="top">
|
<td width="54" valign="top">
|
||||||
<a href="/sysop">
|
<a href="{$sxModel->getURL()}">
|
||||||
<img src="{$dat->getModel(1)->getAvatarUrl('miniscule')}" width=50 />
|
<img src="{$sxModel->getAvatarUrl('miniscule')}" width=50 />
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
<td width="100%" valign="top">
|
<td width="100%" valign="top">
|
||||||
|
@ -53,4 +58,4 @@
|
||||||
{include "../components/nothing.xml"}
|
{include "../components/nothing.xml"}
|
||||||
{/ifset}
|
{/ifset}
|
||||||
{/if}
|
{/if}
|
||||||
{/block}
|
{/block}
|
||||||
|
|
|
@ -21,7 +21,12 @@
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td width="120" valign="top"><span class="nobold">{_photo}:</span></td>
|
<td width="120" valign="top"><span class="nobold">{_photo}:</span></td>
|
||||||
<td><input type="file" name="blob" accept="image/*" /></td>
|
<td>
|
||||||
|
<label class="button" style="">{_browse}
|
||||||
|
<input type="file" id="blob" name="blob" style="display: none;" onchange="filename.innerHTML=blob.files[0].name" />
|
||||||
|
</label>
|
||||||
|
<div id="filename" style="margin-top: 10px;"></div>
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td width="120" valign="top"></td>
|
<td width="120" valign="top"></td>
|
||||||
|
|
|
@ -1,11 +1,7 @@
|
||||||
{extends "../@listView.xml"}
|
{extends "../@layout.xml"}
|
||||||
|
|
||||||
{block title}
|
{block title}
|
||||||
{if $type === "users"}
|
{tr("search_for_$type")}
|
||||||
{tr("search_for_people")}
|
|
||||||
{else}
|
|
||||||
{tr("search_for_groups")}
|
|
||||||
{/if}
|
|
||||||
{if $_GET['query']}
|
{if $_GET['query']}
|
||||||
- {$_GET['query']}
|
- {$_GET['query']}
|
||||||
{/if}
|
{/if}
|
||||||
|
@ -13,41 +9,17 @@
|
||||||
|
|
||||||
{block header}
|
{block header}
|
||||||
{=OPENVK_ROOT_CONF["openvk"]["appearance"]["name"]} »
|
{=OPENVK_ROOT_CONF["openvk"]["appearance"]["name"]} »
|
||||||
{if $type === "users"}
|
{tr("search_for_$type")}
|
||||||
{tr("search_for_people")}
|
|
||||||
{else}
|
|
||||||
{tr("search_for_groups")}
|
|
||||||
{/if}
|
|
||||||
{/block}
|
{/block}
|
||||||
|
|
||||||
{* BEGIN ELEMENTS DESCRIPTION *}
|
{* BEGIN ELEMENTS DESCRIPTION *}
|
||||||
|
|
||||||
{block tabs}
|
|
||||||
<div {if $type === "users"}id="activetabs"{/if} class="tab">
|
|
||||||
<a {if $type === "users"}id="act_tab_a"{/if} href="/search?type=users{if $_GET['query']}&query={urlencode($_GET['query'])}{/if}">
|
|
||||||
{_users}
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
<div {if $type === "groups"}id="activetabs"{/if} class="tab">
|
|
||||||
<a {if $type === "groups"}id="act_tab_a"{/if} href="/search?type=groups{if $_GET['query']}&query={urlencode($_GET['query'])}{/if}">
|
|
||||||
{_groups}
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<form class="header_search_inputbt">
|
|
||||||
<input name="type" type="hidden" value="{$type ?? 'users'}" />
|
|
||||||
<input name="query" class="header_search_input" placeholder="{_search_placeholder}" value="{$_GET['query'] ?? ''}" />
|
|
||||||
<button class="button_search">{_search_button}</button>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
<p style="margin-left: 15px; margin-top: 0;">
|
|
||||||
<b>{tr("results", $count)}</b>
|
|
||||||
</p>
|
|
||||||
<div>
|
|
||||||
{/block}
|
|
||||||
|
|
||||||
{block link|strip|stripHtml}
|
{block link|strip|stripHtml}
|
||||||
{$x->getURL()}
|
{if $type != "apps"}
|
||||||
|
{$x->getURL()}
|
||||||
|
{else}
|
||||||
|
/app{$x->getId()}
|
||||||
|
{/if}
|
||||||
{/block}
|
{/block}
|
||||||
|
|
||||||
{block preview}
|
{block preview}
|
||||||
|
@ -55,17 +27,72 @@
|
||||||
{/block}
|
{/block}
|
||||||
|
|
||||||
{block name}
|
{block name}
|
||||||
<text style="overflow: hidden;"> {$x->getCanonicalName()}</text>
|
{if $type != "apps"}
|
||||||
<img n:if="$x->isVerified()"
|
<text style="overflow: hidden;"> {$x->getCanonicalName()}
|
||||||
class="name-checkmark"
|
{if $_GET['sort'] == "rating"}({$x->getRating()}%)
|
||||||
src="/assets/packages/static/openvk/img/checkmark.png"
|
{elseif $_GET['sort'] == "id"}(id{$x->getId()}){/if}</text>
|
||||||
/>
|
<img n:if="$x->isVerified()"
|
||||||
|
class="name-checkmark"
|
||||||
|
src="/assets/packages/static/openvk/img/checkmark.png"
|
||||||
|
/>
|
||||||
|
{else}
|
||||||
|
{$x->getName()}
|
||||||
|
{/if}
|
||||||
{/block}
|
{/block}
|
||||||
|
|
||||||
{block description}
|
{block description}
|
||||||
<table class="ugc-table">
|
<table class="ugc-table">
|
||||||
<tbody>
|
<tbody>
|
||||||
{if $type === "users"}
|
{if $type === "users"}
|
||||||
|
{if !is_null($_GET['status']) && $_GET['status'] != ""}
|
||||||
|
<tr>
|
||||||
|
<td><span class="nobold">{_status}:</span></td>
|
||||||
|
<td>{$x->getStatus()}</td>
|
||||||
|
</tr>
|
||||||
|
{/if}
|
||||||
|
{if !is_null($_GET['city']) && $_GET['city'] != "" && $x->getPrivacySetting("page.info.read") > 1}
|
||||||
|
<tr>
|
||||||
|
<td><span class="nobold">{_city}:</span></td>
|
||||||
|
<td>{$x->getCity()}</td>
|
||||||
|
</tr>
|
||||||
|
{/if}
|
||||||
|
{if !is_null($_GET['city']) && $_GET['hometown'] != "" && $x->getPrivacySetting("page.info.read") > 1}
|
||||||
|
<tr>
|
||||||
|
<td><span class="nobold">{_hometown}:</span></td>
|
||||||
|
<td>{$x->getHometown()}</td>
|
||||||
|
</tr>
|
||||||
|
{/if}
|
||||||
|
{if !is_null($_GET['politViews']) && $_GET['politViews'] != 0 && $x->getPrivacySetting("page.info.read") > 1}
|
||||||
|
<tr>
|
||||||
|
<td><span class="nobold">{_politViews}:</span></td>
|
||||||
|
<td>{tr("politViews_".$x->getPoliticalViews())}</td>
|
||||||
|
</tr>
|
||||||
|
{/if}
|
||||||
|
{if !is_null($_GET['email']) && $_GET['email'] != "" && $x->getPrivacySetting("page.info.read") > 1}
|
||||||
|
<tr>
|
||||||
|
<td><span class="nobold">{_email}:</span></td>
|
||||||
|
<td>{$x->getContactEmail()}</td>
|
||||||
|
</tr>
|
||||||
|
{/if}
|
||||||
|
{if !is_null($_GET['telegram']) && $_GET['telegram'] != "" && $x->getPrivacySetting("page.info.read") > 1}
|
||||||
|
<tr>
|
||||||
|
<td><span class="nobold">{_telegram}:</span></td>
|
||||||
|
<td><a href="tg://resolve?domain={$x->getTelegram()}">@{$x->getTelegram()}</a></td>
|
||||||
|
</tr>
|
||||||
|
{/if}
|
||||||
|
{if !is_null($_GET['site']) && $_GET['site'] != "" && $x->getPrivacySetting("page.info.read") > 1}
|
||||||
|
<tr>
|
||||||
|
<td><span class="nobold">{_personal_website}:</span></td>
|
||||||
|
<td><a href="{$x->getWebsite()}">{$x->getWebsite()}</a></td>
|
||||||
|
</tr>
|
||||||
|
{/if}
|
||||||
|
{if !is_null($_GET['address']) && $_GET['address'] != "" && $x->getPrivacySetting("page.info.read") > 1}
|
||||||
|
<tr>
|
||||||
|
<td><span class="nobold">{_address}:</span></td>
|
||||||
|
<td>{$x->getPhysicalAddress()}</td>
|
||||||
|
</tr>
|
||||||
|
{/if}
|
||||||
|
{if $x->getPrivacySetting("page.info.read") > 1}
|
||||||
<tr>
|
<tr>
|
||||||
<td><span class="nobold">{_gender}: </span></td>
|
<td><span class="nobold">{_gender}: </span></td>
|
||||||
<td>{$x->isFemale() ? tr("female") : tr("male")}</td>
|
<td>{$x->isFemale() ? tr("female") : tr("male")}</td>
|
||||||
|
@ -78,15 +105,255 @@
|
||||||
<td><span class="nobold">{_registration_date}: </span></td>
|
<td><span class="nobold">{_registration_date}: </span></td>
|
||||||
<td>{$x->getRegistrationTime()}</td>
|
<td>{$x->getRegistrationTime()}</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
{/if}
|
||||||
{/if}
|
{/if}
|
||||||
<tr>
|
<tr>
|
||||||
|
{if !empty($x->getDescription())}
|
||||||
|
{if $type != "apps"}
|
||||||
<td>
|
<td>
|
||||||
<span class="nobold">{_description}:</span>
|
<span class="nobold">{_description}:</span>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
{/if}
|
||||||
|
<td {if $type == "apps"}style="width:400px"{/if}>
|
||||||
{$x->getDescription() ?? '(' . tr("none") . ')'}
|
{$x->getDescription() ?? '(' . tr("none") . ')'}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
{/if}
|
||||||
|
{if $type == "groups"}
|
||||||
|
<td>
|
||||||
|
<span class="nobold">{_size}:</span>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{tr("participants", $x->getFollowersCount())}
|
||||||
|
</td>
|
||||||
|
{/if}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
{/block}
|
{/block}
|
||||||
|
|
||||||
|
{block content}
|
||||||
|
<style>
|
||||||
|
.comment, .post-divider
|
||||||
|
{
|
||||||
|
border:none;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<div style="margin-top:-7px">
|
||||||
|
{include "../components/paginator.xml", conf => (object) [
|
||||||
|
"page" => $page,
|
||||||
|
"count" => $count,
|
||||||
|
"amount" => sizeof($data),
|
||||||
|
"perPage" => $perPage ?? OPENVK_DEFAULT_PER_PAGE,
|
||||||
|
"atBottom" => false,
|
||||||
|
]}
|
||||||
|
</div>
|
||||||
|
<p style="margin-left: 15px; margin-top: 0;">
|
||||||
|
<b>{tr("results", $count)}</b>
|
||||||
|
</p>
|
||||||
|
<div>
|
||||||
|
{include searchOptions}
|
||||||
|
|
||||||
|
{var $data = is_array($iterator) ? $iterator : iterator_to_array($iterator)}
|
||||||
|
<div class="container_gray borderup" style="float:left;width:73.3%;">
|
||||||
|
{if sizeof($data) > 0}
|
||||||
|
{if $type == "users" || $type == "groups" || $type == "apps"}
|
||||||
|
<div class="content" n:foreach="$data as $dat">
|
||||||
|
<table>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td valign="top">
|
||||||
|
<a href="{include link, x => $dat}">
|
||||||
|
{include preview, x => $dat}
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td valign="top" style="width: 100%">
|
||||||
|
{ifset infotable}
|
||||||
|
{include infotable, x => $dat}
|
||||||
|
{else}
|
||||||
|
<a href="{include link, x => $dat}">
|
||||||
|
<b>
|
||||||
|
{include name, x => $dat}
|
||||||
|
</b>
|
||||||
|
</a>
|
||||||
|
<br/>
|
||||||
|
{include description, x => $dat}
|
||||||
|
{/ifset}
|
||||||
|
</td>
|
||||||
|
<td n:ifset="actions" valign="top" class="action_links" style="width: 150px;">
|
||||||
|
{include actions, x => $dat}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
{elseif $type == "posts"}
|
||||||
|
<div n:foreach="$data as $dat" class="content">
|
||||||
|
{if !$dat || $dat->getTargetWall() < 0 && $dat->getWallOwner()->isHideFromGlobalFeedEnabled()}
|
||||||
|
{_closed_group_post}.
|
||||||
|
{else}
|
||||||
|
{include "../components/post.xml", post => $dat, commentSection => true, onWallOf => true}
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
{elseif $type == "comments"}
|
||||||
|
<div n:foreach="$data as $dat" class="content">
|
||||||
|
{if !$dat->getTarget() || $dat->getTarget()->isDeleted()}
|
||||||
|
{_deleted_target_comment}.
|
||||||
|
{else}
|
||||||
|
{include "../components/comment.xml", comment => $dat, linkW => true}
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
{elseif $type == "videos"}
|
||||||
|
{foreach $data as $dat}
|
||||||
|
<div class="content">
|
||||||
|
{include "../components/video.xml", video => $dat}
|
||||||
|
</div>
|
||||||
|
{/foreach}
|
||||||
|
{elseif $type == "audios"}
|
||||||
|
хуй
|
||||||
|
{/if}
|
||||||
|
{else}
|
||||||
|
{ifset customErrorMessage}
|
||||||
|
{include customErrorMessage}
|
||||||
|
{else}
|
||||||
|
{include "../components/nothing.xml"}
|
||||||
|
{/ifset}
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
<script>
|
||||||
|
window.addEventListener("load", (event) => {
|
||||||
|
document.getElementsByClassName("container_gray")[0].style.minHeight = document.getElementsByClassName("searchOptions")[0].clientHeight+"px";
|
||||||
|
document.getElementsByClassName("searchOptions")[0].style.minHeight = document.getElementsByClassName("container_gray")[0].clientHeight-3+"px";
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
{/block}
|
||||||
|
|
||||||
|
{block searchOptions}
|
||||||
|
<div class="searchOptions">
|
||||||
|
<ul class="searchList">
|
||||||
|
<li {if $type === "users"} id="used"{/if}><a href="/search?type=users{if $_GET['query']}&query={urlencode($_GET['query'])}{/if}&sort=id"> {_s_people} {if $type === "users"} ({$count}){/if}</a></li>
|
||||||
|
<li {if $type === "groups"} id="used"{/if}><a href="/search?type=groups{if $_GET['query']}&query={urlencode($_GET['query'])}{/if}&sort=id"> {_s_groups} {if $type === "groups"} ({$count}){/if}</a></li>
|
||||||
|
<li {if $type === "comments"}id="used"{/if}><a href="/search?type=comments{if $_GET['query']}&query={urlencode($_GET['query'])}{/if}&sort=id">{_s_comments} {if $type === "comments"}({$count}){/if}</a></li>
|
||||||
|
<li {if $type === "posts"} id="used"{/if}><a href="/search?type=posts{if $_GET['query']}&query={urlencode($_GET['query'])}{/if}&sort=id"> {_s_posts} {if $type === "posts"} ({$count}){/if}</a></li>
|
||||||
|
<li {if $type === "videos"} id="used"{/if}><a href="/search?type=videos{if $_GET['query']}&query={urlencode($_GET['query'])}{/if}&sort=id"> {_s_videos} {if $type === "videos"} ({$count}){/if}</a></li>
|
||||||
|
<li {if $type === "apps"} id="used"{/if}><a href="/search?type=apps{if $_GET['query']}&query={urlencode($_GET['query'])}{/if}&sort=id"> {_s_apps} {if $type === "apps"} ({$count}){/if}</a></li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<div class="searchOption">
|
||||||
|
<div class="searchOptionName" id="n_sort" onclick="hideParams('sort')"><img src="/assets/packages/static/openvk/img/hide.png" class="searchHide">{_s_order_by}</div>
|
||||||
|
<div class="searchOptionBlock" id="s_sort">
|
||||||
|
<select name="sort" form="searcher" id="sortyor">
|
||||||
|
<option value="id" {if $_GET["sort"] == "name"}selected{/if}>{_s_order_by_id}</option>
|
||||||
|
{if $type == "users"}
|
||||||
|
<option value="name" {if $_GET["sort"] == "name"}selected{/if}>{_s_order_by_name}</option>
|
||||||
|
{if OPENVK_ROOT_CONF["openvk"]["preferences"]["commerce"]}
|
||||||
|
<option value="rating" {if $_GET["sort"] == "rating"}selected{/if}>{_s_order_by_rating}</option>
|
||||||
|
{/if}
|
||||||
|
{/if}
|
||||||
|
</select>
|
||||||
|
<div id="invertor">
|
||||||
|
<input type="checkbox" name="invert" value="1" form="searcher" {if !is_null($_GET['invert']) && $_GET['invert'] == "1"}checked{/if}>{_s_order_invert}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{if $type !== "groups" && $type !== "apps"} {* В OpenVK не сохраняется дата создания групп и приложений *}
|
||||||
|
<div class="searchOption">
|
||||||
|
<div class="searchOptionName" id="n_dates" onclick="hideParams('dates')"><img src="/assets/packages/static/openvk/img/hide.png" class="searchHide">{_s_by_date}</div>
|
||||||
|
<div class="searchOptionBlock" id="s_dates">
|
||||||
|
{if $type != "users"}
|
||||||
|
<p id="bydate">{_s_date_before}:<br>
|
||||||
|
{else}
|
||||||
|
<p id="bydate">{_s_registered_before}:<br>
|
||||||
|
{/if}
|
||||||
|
<input type="date" name="datebefore"
|
||||||
|
form="searcher"
|
||||||
|
id="bydate"
|
||||||
|
style="width:100%"
|
||||||
|
value="{if !is_null($_GET['datebefore'])}{$_GET['datebefore']}{/if}"
|
||||||
|
min="2019-11-19"
|
||||||
|
max={date('Y-m-d')}></p>
|
||||||
|
{if $type != "users"}
|
||||||
|
<p id="bydate">{_s_date_after}:<br>
|
||||||
|
{else}
|
||||||
|
<p id="bydate">{_s_registered_after}:<br>
|
||||||
|
{/if}
|
||||||
|
<input type="date" name="dateafter"
|
||||||
|
form="searcher"
|
||||||
|
style="width:100%"
|
||||||
|
id="bydate"
|
||||||
|
value="{if !is_null($_GET['dateafter'])}{$_GET['dateafter']}{/if}"
|
||||||
|
min="2019-11-18"
|
||||||
|
max={date('Y-m-d')}></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
{if $type === "users"}
|
||||||
|
<div class="searchOption">
|
||||||
|
<div class="searchOptionName" id="n_main" onclick="hideParams('main')"><img src="/assets/packages/static/openvk/img/hide.png" class="searchHide">{_main}</div>
|
||||||
|
<div class="searchOptionBlock" id="s_main">
|
||||||
|
<input type="text" value="{if !is_null($_GET['status'])}{$_GET['status']}{/if}" form="searcher" placeholder="{_status}" name="status">
|
||||||
|
<input type="text" value="{if !is_null($_GET['city'])}{$_GET['city']}{/if}" form="searcher" placeholder="{_city}" name="city">
|
||||||
|
<input type="text" value="{if !is_null($_GET['hometown'])}{$_GET['hometown']}{/if}" form="searcher" placeholder="{_hometown}" name="hometown">
|
||||||
|
<input name="is_online" type="checkbox" {if !is_null($_GET['is_online']) && $_GET['is_online'] == "1"}checked{/if} form="searcher" value="1">{_s_now_on_site}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="searchOption">
|
||||||
|
<div class="searchOptionName" id="n_gender" onclick="hideParams('gender')"><img src="/assets/packages/static/openvk/img/hide.png" class="searchHide">{_gender}</div>
|
||||||
|
<div class="searchOptionBlock" id="s_gender">
|
||||||
|
<p><input type="radio" form="searcher" id="gend" {if $_GET['gender'] == 0}checked{/if} name="gender" value="0">{_male}</p>
|
||||||
|
<p><input type="radio" form="searcher" id="gend1"{if $_GET['gender'] == 1}checked{/if} name="gender" value="1">{_female}</p>
|
||||||
|
<p><input type="radio" form="searcher" id="gend2"{if $_GET['gender'] == 2 || is_null($_GET['gender'])}checked{/if} name="gender" value="2">{_s_any}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="searchOption">
|
||||||
|
<div class="searchOptionName" id="n_relationship" onclick="hideParams('relationship')"><img src="/assets/packages/static/openvk/img/hide.png" class="searchHide">{ovk_proc_strtr(tr("relationship"), 14)}</div>
|
||||||
|
<div class="searchOptionBlock" id="s_relationship">
|
||||||
|
<select name="maritalstatus" form="searcher">
|
||||||
|
<option n:foreach="range(0, 8) as $i" value="{$i}" {if $_GET['maritalstatus'] == $i}selected{/if} form="searcher">
|
||||||
|
{tr("relationship_".$i)}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="searchOption">
|
||||||
|
<div class="searchOptionName" id="n_politViews" onclick="hideParams('politViews')"><img src="/assets/packages/static/openvk/img/hide.png" class="searchHide">{_politViews}</div>
|
||||||
|
<div class="searchOptionBlock" id="s_politViews">
|
||||||
|
<select name="politViews" form="searcher">
|
||||||
|
<option n:foreach="range(0, 9) as $i" value="{$i}" {if $_GET['politViews'] == $i}selected{/if} form="searcher">
|
||||||
|
{tr("politViews_".$i)}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="searchOption">
|
||||||
|
<div class="searchOptionName" id="n_contacts" onclick="hideParams('contacts')"><img src="/assets/packages/static/openvk/img/hide.png" class="searchHide">{_contacts}</div>
|
||||||
|
<div class="searchOptionBlock" id="s_contacts">
|
||||||
|
<input type="text" name="email" value="{if !is_null($_GET['email'])}{$_GET['email']}{/if}" form="searcher" placeholder="{_email}">
|
||||||
|
<input type="text" name="telegram" value="{if !is_null($_GET['telegram'])}{$_GET['telegram']}{/if}" form="searcher" placeholder="{_telegram}"><br id="contacts">
|
||||||
|
<input type="text" name="site" value="{if !is_null($_GET['site'])}{$_GET['site']}{/if}" form="searcher" placeholder="{_personal_website}"><br id="contacts">
|
||||||
|
<input type="text" name="address" value="{if !is_null($_GET['address'])}{$_GET['address']}{/if}" form="searcher" placeholder="{_address}">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="searchOption">
|
||||||
|
<div class="searchOptionName" id="n_interests" onclick="hideParams('interests')"><img src="/assets/packages/static/openvk/img/hide.png" class="searchHide">{_interests}</div>
|
||||||
|
<div class="searchOptionBlock" id="s_interests">
|
||||||
|
<input type="text" value="{if !is_null($_GET['interests'])}{$_GET['interests']}{/if}" form="searcher" placeholder="{_interests}" name="interests">
|
||||||
|
<input type="text" value="{if !is_null($_GET['fav_mus'])}{$_GET['fav_mus']}{/if}" form="searcher" placeholder="{_favorite_music}" name="fav_mus">
|
||||||
|
<input type="text" value="{if !is_null($_GET['fav_films'])}{$_GET['fav_films']}{/if}" form="searcher" placeholder="{_favorite_films}" name="fav_films">
|
||||||
|
<input type="text" value="{if !is_null($_GET['fav_shows'])}{$_GET['fav_shows']}{/if}" form="searcher" placeholder="{_favorite_shows}" name="fav_shows">
|
||||||
|
<input type="text" value="{if !is_null($_GET['fav_books'])}{$_GET['fav_books']}{/if}" form="searcher" placeholder="{_favorite_books}" name="fav_books">
|
||||||
|
<input type="text" value="{if !is_null($_GET['fav_quote'])}{$_GET['fav_quote']}{/if}" form="searcher" placeholder="{_favorite_quotes}" name="fav_quote">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!--<input name="with_photo" type="checkbox" {if !is_null($_GET['with_photo']) && $_GET['with_photo'] == "on"}checked{/if} form="searcher">{_s_with_photo}-->
|
||||||
|
{/if}
|
||||||
|
<input class="button" type="button" id="dnt" value="{_reset}" onclick="resetSearch()">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/block}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue