Jump to content

Search the Community

Showing results for tags 'customization'.

  • Search By Tags

    Type tags separated by commas.
  • Search By Author

Content Type


  • General Discussion
    • Announcements
    • Business Discussion
    • Business Services
    • Sell and Buy
  • Themes & templates
    • KVS default theme
    • KVS paysite theme
  • Educational / support
    • Technical Support
    • FAQ
    • Educational Series

Find results in...

Find results that contain...

Date Created

  • Start


Last Updated

  • Start


Filter by number of...


  • Start



About Me

Found 11 results

  1. KVS supports ability to sell subscriptions to individual profiles and channels. These subscriptions work using the internal token system and have the following features: A subscription can be for unlimited period, or for the given period of days. It is possible to specify only 1 period, which will affect all subscriptions of the same type (e.g. you can configure 1 period for subscription on profiles, and another period for subscription on channels). A subscription should be paid in tokens, so that users first need to purchase tokens using one of the supported and configured payment processor. Subscription means premium access to all videos uploaded by the given profiles or into the given channel. Subscription price can be set in admin panel for each profile / channel separately, or in site frontend in profile / channel editor (this field should be added into theme layout, not exist by default). So ultimately each member can have multiple paid channels with different prices, as well as separate price for the whole member's profile. If subscription is durable, at the end of subscription period KVS will try to deduct the needed amount of tokens from the user's account (e.g. rebill the subscription). If there will be no tokens on user's balance within 24 hours, the subscription will not be renewed. Unfortunately at the time of writing (5.5.1 KVS version) there is no support for sending emails, alerts to users at the moment with regard to this issue. Will be added in future. It is possible to configure that users who sell subscriptions to their profiles or channels will get some % of revenue in tokens paid by buyers. For simplicity this guide will consider the case when you activate only 1 type of subscription at a time: either channels or profiles. If you want to consider having both profile and channel subscriptions at a time, you will need more robust template coding to render video purchase forms correctly, because the same video can be accessed via channel subscription, or via profile subscription and thus 2 purchase options should be displayed. However the provided examples give the full code needed to create both cases and only minor IF / ELSE adjustments are needed. If you want to start using this feature you should first consider configuring a payment processor in Memberzone -> Billings. You need to choose a processor that supports tokens: SegPay, Paypal and Verotel at the moment + CoinPayments added in the new version already. For configuring them please create support ticket after you have your account approved at billing's side. Activating paid subscriptions in admin panel The very few and easy settings are located in Settings -> Memberzone Settings under Paid subscriptions section. You need to activate paid subscriptions for profiles and/or channels, set their default price as 0 and subscription duration as needed (leave empty for unlimited duration). In the next section you can also activate earning tokens from selling profiles and/or channels and specify your commission. Here is how the settings will look like for 30-day channel subscriptions with users earning 90% of all paid tokens (10% is your service fee): Adding fields for users to specify subscription price There could be 2 places where you may need to add this field based on whether you plan profile and/or channel subscriptions. For channels: Go to Website UI -> Language files -> default and add the following lines: edit_channel.field_tokens_required = Price in tokens edit_channel.field_tokens_required_hint = specify price for paid channels Then go to Website UI -> Global blocks -> Edit Channel Form and add the following code into the desired place to render token price field: <div class="row"> <label for="edit_channel_tokens_required" class="field-label">{{$lang.edit_channel.field_tokens_required}}</label> <input type="number" name="tokens_required" id="edit_channel_tokens_required" class="textfield" value="{{if $smarty.post.tokens_required>0}}{{$smarty.post.tokens_required}}{{/if}}" min="0" placeholder="{{$lang.edit_channel.field_tokens_required_hint}}" {{if $is_locked=='true'}}readonly{{/if}}/> <div class="field-error down"></div> </div> For profiles: Go to Website UI -> Language files -> default and add the following lines: edit_profile.field_tokens_required = Price in tokens edit_profile.field_tokens_required_hint = specify price for paid profiles Then go to Website UI -> Theme settings and under Profile options field find the active link (in some themes there could be several profile editing forms and only one of them is active). Then in the profile edit block settings add the following code into template where you want it to be displayed: <div class="row"> <label for="edit_profile_tokens_required" class="field-label">{{$lang.edit_profile.field_tokens_required}}</label> <input type="number" name="tokens_required" id="edit_profile_tokens_required" class="textfield" value="{{if $smarty.post.tokens_required>0}}{{$smarty.post.tokens_required}}{{/if}}" min="0" placeholder="{{$lang.edit_profile.field_tokens_required_hint}}" {{if $is_locked=='true'}}readonly{{/if}}/> <div class="field-error down"></div> </div> Closing access to videos in paid channels / from paid profiles Go to Website UI -> Language files -> default and add the following lines: videos.video_player_guest.channel = This video belongs to a premium channel %CHANNEL%. Only active members can watch videos from premium channels. <br/><br/> Please <a href="%LOGIN%" data-fancybox="ajax">log in</a> or <a href="%SIGNUP%" data-fancybox="ajax">sign up</a> for free. videos.video_player_tokens_not_enough.channel = This video belongs to a premium channel %CHANNEL%. To have access to videos in this channel you must spend <em>%TOKENS_COST%</em> tokens. <br/><br/> Your current tokens balance is <em>%TOKENS_AVAILABLE%</em> tokens. <br/> You need <em>%TOKENS_LEFT%</em> more tokens. videos.video_player_tokens_purchase.channel = This video belongs to a premium channel %CHANNEL%. To have access to videos in this channel you must spend <em>%TOKENS_COST%</em> tokens. <br/><br/> Your current tokens balance is <em>%TOKENS_AVAILABLE%</em> tokens. <br/> Please confirm spending <em>%TOKENS_COST%</em> tokens on this video. Now you need to modify video view block template in Website UI -> Pages -> View Video page -> Video View block. Locate the following line: {{elseif $data.can_watch==0}} and you need to add the following block RIGHT before this line. ATTENTION! This is the most complicated part, as you need to insert the fully copied code fragment into the specific part of Smarty template, failing to do correctly make break your video page. Please make sure you do a backup copy of original template code before you do any changes, just in case you need to restore it. For channels: {{elseif $data.dvd.tokens_required>0 && $data.dvd.is_purchased==0 && $data.user_id!=$smarty.session.user_id && $data.dvd.user_id!=$smarty.session.user_id}} <div class="no-player" style="width: 100%; height: 0; padding-bottom: {{$player_size[1]/$player_size[0]*100|replace:",":"."}}%"> <img src="{{$flashvars.preview_url}}" width="{{$player_size[0]}}" height="{{$player_size[1]}}" alt="{{$data.title}}"/> <span class="message"> {{if $smarty.session.user_id<1}} {{$lang.videos.video_player_guest.channel|replace:"%CHANNEL%":$data.dvd.title|smarty:nodefaults|replace:"%LOGIN%":$lang.urls.login|replace:"%SIGNUP%":$lang.urls.signup}} {{else}} {{if $smarty.session.tokens_available<$data.dvd.tokens_required}} {{assign var="tokens_left" value=$data.dvd.tokens_required-$smarty.session.tokens_available}} {{$lang.videos.video_player_tokens_not_enough.channel|replace:"%CHANNEL%":$data.dvd.title|replace:"%TOKENS_COST%":$data.dvd.tokens_required|replace:"%TOKENS_AVAILABLE%":$smarty.session.tokens_available|replace:"%TOKENS_LEFT%":$tokens_left|replace:"%UPGRADE%":$lang.urls.upgrade|smarty:nodefaults}} <form> <input type="button" class="submit" data-fancybox="ajax" data-href="{{$lang.urls.upgrade}}" value="{{$lang.videos.video_player_tokens_btn_buy|replace:"%TOKENS_COST%":$data.dvd.tokens_required|replace:"%TOKENS_AVAILABLE%":$smarty.session.tokens_available|replace:"%TOKENS_LEFT%":$tokens_left}}"> </form> {{else}} {{assign var="tokens_left" value=$smarty.session.tokens_available-$data.dvd.tokens_required}} {{$lang.videos.video_player_tokens_purchase.channel|replace:"%CHANNEL%":$data.dvd.title|replace:"%TOKENS_COST%":$data.dvd.tokens_required|replace:"%TOKENS_AVAILABLE%":$smarty.session.tokens_available|replace:"%TOKENS_LEFT%":$tokens_left|replace:"%UPGRADE%":$lang.urls.upgrade|smarty:nodefaults}} <form action="{{$data.canonical_url}}" method="post" data-form="ajax"> <div class="generic-error hidden"></div> <input type="hidden" name="action" value="subscribe"/> <input type="hidden" name="subscribe_dvd_id" value="{{$data.dvd.dvd_id}}"> <input type="submit" class="submit" value="{{$lang.videos.video_player_tokens_btn_spend|replace:"%TOKENS_COST%":$data.dvd.tokens_required|replace:"%TOKENS_AVAILABLE%":$smarty.session.tokens_available|replace:"%TOKENS_LEFT%":$tokens_left}}"> </form> {{/if}} {{/if}} </span> </div> For profiles: {{elseif $data.user.tokens_required>0 && $data.user.is_purchased==0 && $data.user_id!=$smarty.session.user_id}} <div class="no-player" style="width: 100%; height: 0; padding-bottom: {{$player_size[1]/$player_size[0]*100|replace:",":"."}}%"> <img src="{{$flashvars.preview_url}}" width="{{$player_size[0]}}" height="{{$player_size[1]}}" alt="{{$data.title}}"/> <span class="message"> {{if $smarty.session.user_id<1}} {{$lang.videos.video_player_guest.channel|replace:"%CHANNEL%":$data.username|smarty:nodefaults|replace:"%LOGIN%":$lang.urls.login|replace:"%SIGNUP%":$lang.urls.signup}} {{else}} {{if $smarty.session.tokens_available<$data.user.tokens_required}} {{assign var="tokens_left" value=$data.user.tokens_required-$smarty.session.tokens_available}} {{$lang.videos.video_player_tokens_not_enough.channel|replace:"%CHANNEL%":$data.username|replace:"%TOKENS_COST%":$data.user.tokens_required|replace:"%TOKENS_AVAILABLE%":$smarty.session.tokens_available|replace:"%TOKENS_LEFT%":$tokens_left|replace:"%UPGRADE%":$lang.urls.upgrade|smarty:nodefaults}} <form> <input type="button" class="submit" data-fancybox="ajax" data-href="{{$lang.urls.upgrade}}" value="{{$lang.videos.video_player_tokens_btn_buy|replace:"%TOKENS_COST%":$data.user.tokens_required|replace:"%TOKENS_AVAILABLE%":$smarty.session.tokens_available|replace:"%TOKENS_LEFT%":$tokens_left}}"> </form> {{else}} {{assign var="tokens_left" value=$smarty.session.tokens_available-$data.user.tokens_required}} {{$lang.videos.video_player_tokens_purchase.channel|replace:"%CHANNEL%":$data.username|replace:"%TOKENS_COST%":$data.user.tokens_required|replace:"%TOKENS_AVAILABLE%":$smarty.session.tokens_available|replace:"%TOKENS_LEFT%":$tokens_left|replace:"%UPGRADE%":$lang.urls.upgrade|smarty:nodefaults}} <form action="{{$data.canonical_url}}" method="post" data-form="ajax"> <div class="generic-error hidden"></div> <input type="hidden" name="action" value="subscribe"/> <input type="hidden" name="subscribe_user_id" value="{{$data.user.user_id}}"> <input type="submit" class="submit" value="{{$lang.videos.video_player_tokens_btn_spend|replace:"%TOKENS_COST%":$data.user.tokens_required|replace:"%TOKENS_AVAILABLE%":$smarty.session.tokens_available|replace:"%TOKENS_LEFT%":$tokens_left}}"> </form> {{/if}} {{/if}} </span> </div> After adding such block, video page should turn into a locked player for paid channels / profiles, unless user purchases them: Displaying subscription status for paid subscription Note: this functionality will become possible in KVS 6.0 update. Default theme renders list of user's subscriptions, but subscriptions can be free and paid. The default rendering doesn't separate them, and here is how to modify that. Go to Website UI -> Language files -> default and add the following lines: subscriptions.list_label_paid_unlimited = (Paid, never expires) subscriptions.list_label_paid_expired = (Paid, expired) subscriptions.list_label_paid_active = (Paid, expires in [count]%1% days || 1: 1 day[/count]) Then go to Website UI -> Pages -> [Memberzone] My Profile page -> My Subscriptions block settings. Locate the following code in its template: <a href="{{$subscribed_url}}" class="title">{{$item.title}}</a> And change it like this: <a href="{{$subscribed_url}}" class="title"> {{$item.title}} {{if $item.purchase.purchase_id>0}} {{if $item.purchase.expiry_date_is_unlimited==1}} {{$lang.subscriptions.list_label_paid_unlimited}} {{elseif $item.purchase.expiry_date_hours_left<0}} {{$lang.subscriptions.list_label_paid_expired}} {{else}} {{assign var="days_left" value=$item.purchase.expiry_date_hours_left/24|floor}} {{$lang.subscriptions.list_label_paid_active|count_format:"%1%":$days_left}} {{/if}} {{/if}} </a> And the result will look like this:
  2. There is no built-in functionality in KVS to require users verify their documents or selfie, but there is alternative way to use Posts functionality for that. Even posts don't allow users to upload files, so it will be required to ask users to upload their documents to their own cloud services and specify secure link to the uploaded file. Here is how to configure that. Creating post type for the task and configuring global block for the users Step 1. Please go to Posts -> Post types and create new post type with the following data: Title: any of your choice, e.g. Identity Confirmation External ID: identity_confirmation Post page URL pattern: 404.php?%ID% Step 2. Go to Website UI -> Global blocks and add block with the following parameters: Block name: Identity Confirmation Block type: post_edit Cache: 86400 (default value) After saving global blocks list you will notice Identity Confirmation block added to the end. Click to edit this block. Step 3. Set the following Template code for this block: {{query_kvs select="single" table="posts_types" where_external_id='identity_confirmation' assign='post_type'}} {{query_kvs select="list" table="posts" where_user_id=$smarty.session.user_id where_post_type_id=$post_type.post_type_id assign='post'}} {{if $async_submit_successful=='true'}} <div class="success" data-fancybox="refresh"> {{$lang.edit_profile.success_message_identity_confirmation_requested}} </div> {{else}} <strong class="popup-title">{{$lang.edit_profile.title_identity_confirmation}}</strong> <div class="popup-holder"> {{if count($post)>0}} <div class="success" data-fancybox="refresh"> {{$lang.edit_profile.success_message_identity_confirmation_requested}} </div> {{elseif $smarty.session.status_id==6}} <div class="success" data-fancybox="refresh"> {{$lang.edit_profile.success_message_identity_confirmed}} </div> {{else}} <form action="{{$lang.urls.identity_confirmation}}" data-form="ajax" method="post"> <div class="generic-error hidden"></div> <p> {{$lang.edit_profile.field_identity_confirmation|replace:"%site%":$lang.project_name|smarty:nodefaults}} </p> <div class="row"> <input type="text" name="content" class="textfield"/> <div class="field-error down"></div> </div> <div class="bottom"> {{if $use_captcha==1}} <label>{{$lang.common_forms.field_captcha_hint}}</label> <div class="captcha-control"> {{if $recaptcha_site_key!=''}} <div data-name="code"> <div data-recaptcha-key="{{$recaptcha_site_key}}" data-recaptcha-theme="{{if $lang.theme.style=='metal'}}dark{{else}}light{{/if}}"></div> <div class="field-error down"></div> </div> {{else}} <div class="image"> <img src="{{$lang.urls.captcha|replace:"%ID%":"signup"}}?rand={{$smarty.now}}" alt="{{$lang.common_forms.field_captcha_image}}"/> <label for="identity_confirmation_code" class="field-label required">{{$lang.common_forms.field_captcha}}</label> <input type="text" name="code" id="identity_confirmation_code" class="textfield" autocomplete="off"/> <div class="field-error up"></div> </div> {{/if}} </div> {{/if}} <input type="hidden" name="action" value="add_new_complete"/> <input type="hidden" name="title" value="Identity Confirmation"/> <input type="submit" class="submit" value="{{$lang.edit_profile.btn_request}}"/> </div> </form> {{/if}} </div> {{/if}} Under block parameters change the following: post_type (String): identity_confirmation duplicates_allowed (On/Off): switch ON optional_description (On/Off): switch ON optional_tags (On/Off): switch ON optional_categories (On/Off): switch ON use_captcha (On/Off): switch ON, if you want users to solve captcha puzzle Step 4. Go to Website UI -> Language files -> default and add the following texts at the end. Feel free to adjust as needed: memberzone.profile_member_action_identity_confirmation = Verify your account here. edit_profile.title_identity_confirmation = Account Verification edit_profile.field_identity_confirmation = Create a selfie of yourself holding an A4 sheet with our site name written by hand:<br/><br/><b>%site%</b><br/><br/>Then upload this image to a cloud of your choice (e.g. Google Docs) and send us link to the uploaded file: edit_profile.success_message_identity_confirmation_requested = Thank you! Your account confirmation request was successfully registered and will be reviewed by our team soon. edit_profile.success_message_identity_confirmed = Your account has already been confirmed. edit_profile.btn_request = Submit Request edit_video.warning_upload = Only verified accounts can upload videos. urls.identity_confirmation = /confirm-account/ Step 5. You need to edit .htaccess file that is located in www root with your FTP client, or any other File Manager that might be provided by your server panel. Add the following line (could be added in the beginning, or next to RewriteRule ^delete-profile/$): RewriteRule ^confirm-account/$ index.php?mode=async&function=get_block&block_id=post_edit_identity_confirmation&global=true [L,QSA] Changing upload block to allow upload only for verified users To be noted, it will be required to use webmaster status to manually control which users are allowed to upload. In member profile editor in admin panel you should manually change member status to webmaster as a result of account verification process. Step 6. In order to change video upload block behavior, please go to Website UI -> Theme settings and scroll down to Functionality options group. Click on Video Edit link there to open video upload block settings. In Template code, your first line should look like this: {{if $async_submit_successful=='true'}} If it has something else, your video edit template is modified and you will need to adjust the proposed code to your template changes. The proposed code is to replace this line with this block: {{if $smarty.session.status_id!=6}} <div class="headline"> <h2> {{$lang.edit_profile.title_identity_confirmation}} </h2> </div> <div class="box"> {{$lang.edit_video.warning_upload}} <a href="{{$lang.urls.identity_confirmation}}" data-fancybox="ajax" style="text-decoration: underline; font-weight: bold">{{$lang.memberzone.profile_member_action_identity_confirmation}}</a> </div> {{elseif $async_submit_successful=='true'}} You may notice that we actually leave the old line almost there, with slightly changed from {{if}} to {{elseif}} syntax. This template change will hide video upload form for non-webmaster users and instead will show a link to verify their account: Step 7. However this may not stop bots, that could upload bypassing the actual site GUI, so you may need to add some small additional protection to make sure that bots can't upload as well. In the same video edit block settings activate the following parameters: max_duration (Integer): 1 max_duration_webmaster (Integer): 9999 These settings mean that all users will not be able to upload virtually any video, because of duration limit of 1 second. While webmaster users will be able to upload videos with duration up to 9999 seconds. So only webmaster users can actually upload videos. Testing all together Now log in with non-webmaster profile and go to upload page. You should see link to verify your account. If you see a white or partial page, you did wrong paste at step #6. You can go to Website UI -> Change history and look at your recent change to video edit block. There you can find original template version and return it back if needed, or check what you did wrong here. By clicking the account verification link you should see the popup to verify account. Submit something into the text field. This should show you success message that your data is submitted. If it shows any error, you did something wrong at step #3 with block parameters. If you go to admin panel start page, you may notice that it will show you alert like the following: Click this link and you will see Account Verification post submitted by you seconds ago. Open it and check the link under Content area. If you want to verify it, you first need to manually open user profile editor for this user and change their status to webmaster. Then approve this account verification post. If you want to reject it, you just need to delete it, so that user can re-submit again. Unfortunately KVS for now doesn't support any automated messages to be sent to users on different admin actions; if you want to notify user about some issue with their verification, you will need to manually submit a message to this user via Memberzone -> Messages.
  3. In KVS there is no built-in support for country restrictions. The main reason is that KVS caching engine doesn't allow having these restrictions along with site caching, so for now we don't have any way to add this feature. However, if you discount caching aspect, it is possible to activate this feature with few theme adjustments. Disabling caching could have negative impact on your database load. But in this case you will only disable cache for video_view block, which is not the hardest block to be rendered. Step 1. First of all you need to switch off caching for the View Video page and video_view block on it. In order to do that go to Website UI -> Pages and search for video_view there. This will show you all pages that have this block (by default your theme will only have one page and block with caching time set to 3600 on both). Set caching time to 0 in both page and block fields (by default it will have 3600) and use Save caching option: Step 2. Go to Settings -> Customization and activate some custom text field in Videos. This will be the field where you need to list country codes of the countries where a specific video should be blocked. The format of the data is comma-separated list of 2-symbol country codes, for example like this: NOTE: the actual formatting doesn't matter, possible to specify in lower case, or without space between commas. Step 3. In Website UI -> Pages -> View Video page -> video_view block template find this fragment: {{if $data.status_id==5 || $data.status_id==2 || $data.status_id==3}} <div class="no-player" style="width: 100%; height: 0; padding-bottom: {{$player_size[1]/$player_size[0]*100|replace:",":"."}}%; background: black"> <span class="message">{{$data.delete_reason|default:$lang.videos.video_player_deleted}}</span> </div> {{elseif $is_limit_over==1}} .... Add the following block right before it: {{assign var="blocked_countries" value=","|explode:$data.custom1}} {{assign var="is_blocked_by_country" value="false"}} {{foreach from=$blocked_countries item="country_code"}} {{if $country_code|trim|strtolower==$smarty.server.GEOIP_COUNTRY_CODE|strtolower}} {{assign var="is_blocked_by_country" value="true"}} {{/if}} {{/foreach}} {{if $is_blocked_by_country=='true'}} <div class="no-player" style="width: 100%; height: 0; padding-bottom: {{$player_size[1]/$player_size[0]*100|replace:",":"."}}%; background: black"> <span class="message">This video is not available in your country</span> </div> and it order to make the smarty syntax complete you should changed {{if}} with {{elseif}} in the mentioned before block in the first line: {{elseif $data.status_id==5 || $data.status_id==2 || $data.status_id==3}} <div class="no-player" style="width: 100%; height: 0; padding-bottom: {{$player_size[1]/$player_size[0]*100|replace:",":"."}}%; background: black"> <span class="message">{{$data.delete_reason|default:$lang.videos.video_player_deleted}}</span> </div> {{elseif $is_limit_over==1}} .... IMPORTANT! If you fail to insert this block into the correct place, or to correct the IF -> ELSEIF logic, your template code may become broken and in this case it will stop rendering player. Then go to Website UI -> Change history to find the previous template version and restore it.
  4. How can I change or resize thumbnails to small or make them like how xhamster.com thumbnails look ?
  5. This article provides step by step instruction on how to get categories dropdown in site header. Please note the CSS styles described here are not 100% perfect, as they in some way overlap with styles for upload item dropdown. Please adjust them as needed by your site design. Step 1. Go to Website UI -> Global blocks and click on Add block button at the bottom. In the new block line specify block name as Header Categories and under block type select list_categories. Save. Step 2. Open the newly appeared Header Categories block for editing. Configure block parameters as needed, e.g. items_per_page if you want to limit the number of categories displayed, and other filters if you want to limit which categories are displayed. Enter the following code into template code area and save: {{if count($data)>0}} <li class="drop"> <span>{{$lang.header.primary_menu_categories}}</span> <ul> {{foreach from=$data item="item"}} <li> <a href="{{$lang.urls.videos_by_category|replace:"%DIR%":$item.dir|replace:"%ID%":$item.category_id}}">{{$item.title}}</a> </li> {{/foreach}} </ul> </li> {{/if}} Step 3. Go to Website UI -> Page components -> include_header_general.tpl for editing. Find the <LI> node that renders categories menu item, similar to this: <li {{if $page_id=='categories_videos' || $page_id=='categories_albums'}}class="selected"{{/if}}> <a href="{{$lang.urls.categories_videos}}" id="item6">{{$lang.header.primary_menu_categories}}</a> </li> And replace it with this block insert: {{insert name="getGlobal" global_id="list_categories_header_categories"}} Step 4. To make styles look better, add the following basic styling into your theme CSS file: .navigation .primary .drop > span { display: block; position: relative; background-color: #e0dfdf; padding: 12px 38px 12px 17px; cursor: pointer; } .navigation .primary .drop > span:after { position: absolute; z-index: 1; content: "\e901"; font-family: "icomoon" !important; speak: none; font-style: normal; font-weight: normal; font-variant: normal; text-transform: none; line-height: 1; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; font-size: 5px; top: calc(50% - 1px); right: 20px; will-change: transform; -webkit-backface-visibility: hidden; backface-visibility: hidden; -webkit-transform: translateY(-50%) rotate(0); transform: translateY(-50%) rotate(0); -webkit-transition: -webkit-transform 0.3s; transition: -webkit-transform 0.3s; transition: transform 0.3s; transition: transform 0.3s, -webkit-transform 0.3s; } .navigation .primary .drop:hover > span { background-color: #276fdb; color: #fff; } .navigation .primary .drop:hover > span::after { -webkit-transform: translateY(-50%) rotate(180deg); transform: translateY(-50%) rotate(180deg); }
  6. List of search queries is displayed by search_results block. Here is how you can put it into the main pages: {{insert name="getBlock" block_id="search_results" block_name="Similar Searches"}} Put it into Index and Common Videos List (renders categories, tags, models, content sources and etc.) page templates. After you add this block into page template and save, it will appear in the list of blocks on this page and you can edit its settings. Here is template code for this block that will render cloud of search queries: {{if count($data)>0}} <div class="box search-cloud"> {{foreach item="item" from=$data}} {{assign var="query" value=$item.query|replace:"-":"[dash]"|replace:"&":"%26"|replace:"?":"%3F"|replace:"/":"%2F"|replace:" ":"-"|replace:"[dash]":"--"|rawurlencode}} <a href="{{$lang.urls.search_query|replace:"%QUERY%":$query}}" style="{{if $item.is_bold==1}}font-weight: bold; {{/if}}{{if $item.size>0}}font-size: {{$item.size}}px;{{/if}}">{{$item.query}}</a> &nbsp;&nbsp;&nbsp; {{/foreach}} </div> {{/if}} In block parameters configure: items_per_page: the number of queries you want to be displayed. sort_by: sorting of the queries. var_category_dir = category var_tag_dir = tag size_from = 12 size_to = 16 Also make sure you put a cache time of 86400, as this block will be quite heavy. When this is configured, you can see query cloud is displayed on Index and other video pages. For tag and category pages it should be relevant to the displayed tag / category. On other pages it may render the same set of queries, as they won't have context object that can be used to display similar queries.
  7. This article explains how to allow site users to switch between different skins using KVS default theme as example. Please note that the same approach can be used for a variety of ideas that involve different CSS styles display in your design, for example: Switching the whole skins Switching between big and small thumbs in video lists Switching between short / detailed video list display layout etc. NOTE: if you are planning to use multiple CSS skins on your site, any CSS customizations should be put and tested with both skins. Otherwise for some users your site may become broken, if skin they are using has not been updated properly to include all the needed customizations. Change KVS default theme to support switchable skins KVS default theme provides 2 skins: dark and white. Skin is configured via theme configuration and the actual skin is rendered in site header template using this Smarty condition: {{if $lang.theme.style=='white'}} ... show white skin {{else}} ... show dark skin {{/if}} Use the following steps to make is adjustable. 1) In Settings -> Website Settings fill in name and default value for one of the free Dynamic HTTP parameters as skin and white (use dark if you want dark skin to be the default one). Leave lifetime value as set to 360 days by default: 2) In Website UI -> Page Components -> include_header_general.tpl change this block: {{if $lang.theme.style=='white'}} <link href="{{$config.statics_url}}/static/styles/all-responsive-white.css?v=7.0" rel="stylesheet" type="text/css"/> <link href="{{$config.statics_url}}/static/styles/jquery.fancybox-white.css?v=7.0" rel="stylesheet" type="text/css"/> {{else}} <link href="{{$config.statics_url}}/static/styles/all-responsive-metal.css?v=7.0" rel="stylesheet" type="text/css"/> <link href="{{$config.statics_url}}/static/styles/jquery.fancybox-metal.css?v=7.0" rel="stylesheet" type="text/css"/> {{/if}} With the same logic using Javascript: <script type="application/javascript"> if ('%skin%' == 'white') { document.write('<link href="{{$config.statics_url}}/static/styles/all-responsive-white.css?v=7.0" rel="stylesheet" type="text/css"/>'); document.write('<link href="{{$config.statics_url}}/static/styles/jquery.fancybox-white.css?v=7.0" rel="stylesheet" type="text/css"/>'); } else { document.write('<link href="{{$config.statics_url}}/static/styles/all-responsive-metal.css?v=7.0" rel="stylesheet" type="text/css"/>'); document.write('<link href="{{$config.statics_url}}/static/styles/jquery.fancybox-metal.css?v=7.0" rel="stylesheet" type="text/css"/>'); } </script> <noscript> <link href="{{$config.statics_url}}/static/styles/all-responsive-metal.css?v=7.0" rel="stylesheet" type="text/css"/> <link href="{{$config.statics_url}}/static/styles/jquery.fancybox-metal.css?v=7.0" rel="stylesheet" type="text/css"/> </noscript> After this change, your site should be displayed with the skin that is configured as default value for skin dynamic HTTP parameter. 3) If you need to render skin switcher, you can do this multiple ways. You can open any URL of your site and pass skin=value parameter in the URL: http://domain.com/?skin=white http://domain.com/?skin=dark The selected value will be remembered in user cookies. Alternatively if you don't want to expose this URL parameter to public, you can set the cookie explicitly via Javascript and refresh the page: $.cookie('kt_rt_skin', 'dark', {expires: 360, path: '/'}); window.location.reload(); When only parts of design should be switchable When you want users to switch only part of display functionality (e.g. bigger thumbs VS smaller thumbs, short list layout VS detailed list layout), it is easier to have only 1 CSS file and customize its display using CSS classes hierarchy, e.g.: .list .thumb { width: 320; height: auto; } .list .small .thumb { width: 240; } With the above approach, all you need is to add small class name to the list HTML element in order to switch it to smaller thumbs. Here is where Dynamic HTTP parameters (Settings -> Website Settings) can be of your service again. Note that this time we don't specify any default value for thumb_size parameter, because bigger thumbs are default behavior and parameter should print empty value for bigger thumbs: Now in template where you render list thumbs (e.g. Website UI -> Page Components -> include_list_videos_block_common.tpl) you should add %thumb_size% token into classes list of root thumb list element similar to this: <div class="list %thumb_size%"> ..rendering thumbs list here </div> Then you can switch between big and small thumbs: http://domain.com/?thumb_size=small http://domain.com/?thumb_size=
  8. Hello. How to show Custom Field on video view page? From "Customization" for example: Identifier = Custom 2 and Field name = Link. I have this field on adding new video page, but there is no such field on video view page... can someone help me whit this please! Thankyou
  9. Theme customization in KVS in most cases won't require any specific programming knowledge except HTML / CSS / JS, which are core web technologies. In addition to that some basic Smarty template language skills are required to be able to generate iterations, conditional rendering (IF / ELSE) and do some basic formatting. Changing design logo, styles and colors Each KVS theme may come with some pre-installed skins, so first of all you should check if there is any skin that closely matches you needs. Please find this in Website UI -> Theme settings page: NOTE: this page also contains a huge list of basic settings that allow you easily switch off or on some site functionality. After you choose the skin you need, the only possible way to edit its look is to modify CSS styles in one of the CSS files used by theme. This will require some basic CSS knowledge for sure. You can find the needed CSS file by looking into the source code of your site index page and searching for rel="stylesheet" (depending on your theme there may be slightly different CSS filenames): <link href="https://www.kvs-demo.com/static/styles/all-responsive-white.css?v=8.1" rel="stylesheet" type="text/css"/> <link href="https://www.kvs-demo.com/static/styles/jquery.fancybox-white.css?v=8.1" rel="stylesheet" type="text/css"/> The majority of design styles is located in all-responsive-white.css file (this filename could be different for other themes). The easiest way to edit styles is to work locally: Open source code of a page you want to modify (typically Index page from the start). You can do that by prefixing browser URL with view-source: view-source:https://www.kvs-demo.com Then copy all the page HTML code and save it into a local HTML file (e.g. index.html). Modify link's href to point to a local CSS file in the same directory: <link href="all-responsive-white.css?v=8.1" rel="stylesheet" type="text/css"/> Copy all-responsive-white.css file from the server into the same directory where you put index.html. Now if you open your local index.html file, your browser should open the same page, but it will use your local CSS file for rendering styles. You can now modify styles in all-responsive-white.css locally without any need of copying it back to server. Use CSS editor of your choice that will make your work much easier. Once your finished your local changes, replace this file on server's FTP with your local copy. Use this approach to modify styling for any page, by saving its local HTML copy and editing CSS file locally. In order to modify site logo, you will typically need to replace logo image file in /static/images directory on your server. There is one logo.png or logo.jpg image file that you need to replace. Changing texts, URLs and SEO There is separate article on this: Working with SEO, texts and URLs in KVS themes. Customizing favicon In order to specify your custom favicon you need to modify this file on your project's FTP: https://kvs-demo.com/favicon.ico You can use online Favicon Generator to create ICO file from your PNG. Adding / removing menu items Many menu items can be controlled from Website UI -> Theme settings page, where you can activate or deactivate some site sections. However if you need to add menu items that are not initially supported by theme, you have to manually modify header template in Website UI -> Page components -> include_header_general.tpl. Please find the following or similar code: <ul class="primary"> <li {{if $page_id=='index'}}class="selected"{{/if}}> <a href="{{$lang.urls.home}}" id="item1">{{$lang.header.primary_menu_home}}</a> </li> ... other menu items using LI tags ... </ul> In order to add new menu items, you should add the following code in the desired position: <li> <a href="https://menuitemurl.com" id="item_custom1">Menu Item Text</a> </li> Then if you want to add some custom styling for it, you can use its ID (item_custom1 in the example) to add some specific styling in theme CSS file (see Changing design logo, styles and colors section above): #item_custom1 { color: red; } Understanding KVS page structure Each URL you see in browser is generated by KVS site engine. A URL is first parsed by special web server module called mod_rewrite and then addressed to one of KVS page definitions. You can find these page definitions in Website UI -> Pages section of KVS admin panel. Every page definition specifies template code that typically has the following components: Setting header variables for page title, description, keywords and so on Site header Main area which may render multiple page blocks or just a piece of static HTML code Site footer Here is example of Index page code, which highlights all of the above components: Site header and footer are inserted by using simple {{include}} statements and this is nothing more than moving the same reusable code fragment into a named component. You can edit them in KVS admin panel under Website UI -> Page components section. Header component supports a number of variables that should be set prior to including it into page definition using {{assign}} statement: page_title page_description page_keywords page_rss page_canonical Their meaning is clear, except probably page_rss and page_canonical. The first one should provide URL for page RSS (if any), and the last one should provide URL for canonical tag of the page. You may also notice that these variables refer some other variables. There is special global $lang variable, which contains all texts from localization (Website UI -> Texts section). All templates refer $lang variables instead of specifying texts directly, so that multiple language support is possible. However you are not required to follow the same concept; it is absolutely legal to specify the actual texts here, honoring Smarty template syntax and using quotes to specify texts: {{assign var="page_title" value="This is the actual title for index page"}} Why these page_xxx variables are needed after all? Header code is located in reusable page component and used on all site pages. But in reality this code is not 100% same for different pages and some parts of it should be different, for example page title. These different pieces are provided via variables, and each page definition provides its own values for these variables. The main area of page structure is where the actual content is being displayed. In KVS everything which is displayed from the database is displayed using specific page blocks. There is a block to display video list, there is a block to display video player, and etc. There are almost 60 different types of blocks. You can find the full list of blocks with their documentation in Website UI -> Page blocks overview. Blocks are the primary building elements of a KVS-powered site. Unlike page components, which are designed to facilitate HTML code reuse, blocks have power to query data from the database using various filtering and sorting options, and display it. At the same time blocks can use page components inside them, because reusable code can be anything, not just header or footer. For example blocks that display different lists use include_pagination_block_common.tpl page component to render pagination controls inside them. If you look into Index page again, you will notice that it contains 2 blocks for rendering 2 separate video lists: Videos Watched Right Now and Most Recent Videos (in default theme). Each of these can be edited separately and defines a huge set of parameters that can be used to tweak block behavior. Parameters are grouped by their common usage sense, for example there are parameters to control lists, such as number of items and sorting. However these parameters never control rendering logic, e.g. which colors should be used (this is controlled by CSS styles) or which fields to render for each video in list (this is controlled by block template). Understanding KVS routing Some pages are designed to serve only 1 URL, for example Index page, which is only used when rendering site index. Another example is [Static] DMCA page, which renders static HTML text. Many of other pages are designed to render similar things for different URLs. For example different video URLs: each of them renders player for different videos, but KVS engine uses the same page definition for these URLs, and the only difference is parameters provided via the URL: https://www.kvs-demo.com/videos/368/dj-manian-ravers-fantasy/ https://www.kvs-demo.com/videos/155/eminem-not-afraid/ These both URLs have the same pattern, but only different in video ID and directory, so they are being routed using the same rule in .htaccess file (this file is processed by web server's rewrite module): RewriteRule ^videos/([0-9]+)/([^/]+)/$ view_video.php?id=$1&dir=$2 [L,QSA] This RewriteRule is basically routing the given URLs into URLs understandable to KVS, and if you try to open them you can see that they display the same content as original URLs: https://www.kvs-demo.com/view_video.php?id=368&dir=dj-manian-ravers-fantasy https://www.kvs-demo.com/view_video.php?id=155&dir=eminem-not-afraid Routing doesn't provide any additional functionality, it just hides the internal page structure and generates nice URLs. KVS provides a simple way to see which page definition serves any specific site URL. This feature is only available for admins, so you should be logged into admin panel to see that. On any site page generated by KVS you should see KVS Admin Toolbar at the bottom, which indicates page definition and its structure. By clicking on edit icon next to page definition name you will open page definition editor: Where to modify a specific section on a specific page Please note that all texts that are displayed can be modified in Website UI -> Texts section, you don't even need to edit code to modify them. You may need to edit code if you want to change the way how data is being displayed, or change HTML code around it. Here are top 6 places where you may need to change something in site layout / contents: Site header. Please go to Website UI -> Page components -> include_header_general.tpl to edit header HTML code. Site footer. Please go to Website UI -> Page components -> include_footer_general.tpl to edit footer HTML code. Video list display. All video lists have the same display, which can be found in Website UI -> Page components -> include_list_videos_block_common.tpl. This component renders list title, sorting controls and the list itself. Video player section. Please go to Website UI -> Pages -> View Video page -> Video View block. Index page contents. Please go to Website UI -> Pages -> Index page. The number of videos displayed in categories, tags, top rated and other. Please go to Website UI -> Pages -> Common Videos List page -> Common Videos List block. Since all these lists are very similar to each other, they are rendered by a single page definition and the only difference is in parameters that are passed there to enable specific filtering (by category, by tag), or specific sorting (by top rated, by most viewed). If you want to change the contents of different popups, such as login, signup and other similar dialogs, please go to Website UI -> Global blocks. All popups in KVS themes are typically rendered by global blocks, as they are not connected to any specific page. If none of the above, then you should use KVS Admin Toolbar to analyze page structure and see which page definition or specifically which block on it you need to change. Toolbar allows you highlight each block and see where it is displayed in your theme design: Understanding template variables KVS doesn't provide list of all variables and their descriptions, there are thousands of them in many contexts. However there are several other ways you can get the info you need. But before we list them, here are some highlights on what are variables in templates. In Smarty template every variable is rendered using this syntax: {{$variable_name}} If a variable is an object, its properties or fields can be accessed using dot syntax: {{$object.property}} There can be multiple levels of nesting here like this: {{$object.another_object.property}} Some variables are arrays (e.g. contain multiple values), and you should iterate over them to access their values. Iteration in Smarty is typically done using {{foreach}} block: {{foreach from=$array_variable item="item"}} {{$item}} - all templates in KVS typically use $item variable to access every item of the iteration {{$item.property}} - in many cases iteration item is an object, so you need to use dot syntax to access its properties {{/foreach}} NOTE: Smarty syntax requires the closing {{/foreach}} for each iteration, if you forget it your template will not be displayed. You can define your own local variables in templates and then use them in some other parts of this template: {{assign var="variable_name" value="variable value"}} ... {{if $variable_name=='variable value'}} ... show something {{/if}} KVS templates typically use local variables for page components, which are reusable parts of HTML code (see Understanding KVS page structure section above). Therefore you can find local variables usage in almost every page definition, that include header component. If you want to use a variable inside string of text, Smarty will require you to use very specific syntax to escape it - using `` quotes. It is a common practice to insert variables into some bigger texts, typically used for SEO. For example consider this: {{assign var="seo_text" value="This is a text with a `$variable` value"}} The other possible way to do the same is to use some placeholder for a variable and replace function to replace placeholder with the actual value: {{assign var="seo_text" value="This is a text with a %1% value"|replace:"%1%":$variable}} The second approach is widely used in KVS templates, because all texts are localized and configured in Website UI -> Texts section and templates use $lang global variable to refer texts by their IDs. In this case introducing placeholders is the only possible option to allow inserting dynamic values into static texts: {{assign var="seo_text" value=$lang.some_text_identifier|replace:"%1%":$variable}} All 3 variants will actually do the same, they will assign "This is a text with a SOMETHING value" into a variable named $seo_text, that can be further used to display its value in this template. It is very important to understand that all variables have certain scope where they are valid to be used: Global variables. These variables can be used globally in any template. This is $config object, which contains all system-specific configuration values that are set in /admin/include/setup.php file. This is $lang variable, which contains all localized texts. Session variables. These variables contain data of the user that is currently logged in (if no user is logged in, then they will be empty). An example of such variable is $smarty.session.username that contain username of the current user. Such variables can be used inside page templates and page components that are included in page templates (typically header and footer). However it is not legal to use session variables inside block templates for most of the block types due to caching specifics. KVS will show caching error if you try to use session variable where not allowed. Request variables. You can also refer URL request parameters in templates globally using $smarty.get.parameter_name. For example when rendering page for this URL: https://www.kvs-demo.com/videos/155/eminem-not-afraid/ you can refer 155 and eminem-not-afraid via certain $smarty.get.xxx variables. However this practice should be avoided and in most cases it is not needed. Not all request variables can be used and this is again connected to caching specifics. KVS will show caching error if you try to use request variable where not allowed. Page variables. These variables can only be used inside page templates and page components that are included in page templates. These are all variables that you set locally in page template via {{assign}} directive. These are also variables, pushed from blocks to their parent page via $storage object. Block variables. Block variables are variables that contain the actual data being displayed. They can only be used inside block templates and page components that are included in block templates. In KVS all blocks are fully isolated from each other and from the page that contains them. You can't use block variables from one block in any other block on the same page. Similar to that, you can't use block variables in parent page template. And vice versa, you can't use page variables in blocks on this page. So before you can use any variables in the place where you want to modify anything, you should clearly understand if this is a page context, or a block context. If this is a block context, which exactly block it is. Which variables should be used in templates As we explained in the previous section, it is important to understand where you are going to put changes. Is this a page template, or a block template, and which exactly block template it is. In most cases you will be editing block templates and page components used inside blocks (for example, include_list_videos_block_common.tpl that renders video list). The first way you can see which variables can be used is to look into default template example of this block type in Website UI -> Page blocks overview. Unfortunately we didn't yet finish updating these templates for all blocks, so you may find it missing the needed data for some block types. Here is how it looks for the block where completed: {{foreach item="item" from=$data}} <a class="item" title="{{$item.title}}" href="{{$config.project_url}}/categories/{{$item.dir}}/"> ID: {{$item.category_id}}<br/> Title: {{$item.title}}<br/> Description: {{$item.description}}<br/> Directory: {{$item.dir}}<br/> Synonyms: {{$item.synonyms}}<br/> Total videos: {{$item.total_videos}}<br/> Today videos: {{$item.today_videos}}<br/> ... </a> {{/foreach}} You can find almost all data values that are supported by block objects, so you just need to find the needed variable and copy it. A more advanced way of finding which variables can be used is provided by debugger: Debugger renders page structure and lists all blocks that are used on this page. For each block it displays a structure of available variables with their ACTUAL values for the given time and URL: On the example screenshot you can see list of variables for the Related Videos block on View Video page. You can see $data variable, which is an array of 12 items (video objects). If you further expand any item you will see all properties of that item: Here we expanded the first video object and we can see all its properties, for example title and video_viewed (the number of views), and etc. Since $data is an array, it is possible to access all array items via their index, e.g. $data.0 will refer the first item and $data.1 will refer the 2nd item. However in real world there is no need to access individual array items, it is typically needed to display them one by one, so here is where {{foreach}} iteration is used: {{foreach from=$data item="item"}} {{$item.title}} - will display Baby Alice - Piña Colada Boy for the first item {{/foreach}} Using debugger this way will help you to understand which variables you can use in any specific block that you see on a page and what their values could be. As we also said in previous section, block variables cannot be used anywhere outside block templates and page components that are used in block templates. However, most blocks will push some variables into special $storage object that can be used in parent page to refer some data from its blocks. These variables are also visible in debugger under Block data in storage: Common Videos List concept To be continued...
  10. Customization is an important part of every modern tube and KVS provides almost unlimited possibilities to customize your video sites. However it is important to understand that there are different customization levels that may or may not affect the future of your project. In KVS we have regular software updates as a part of our product life cycle. Each update brings dozens of new features and bugfixes in multiple spheres; and many of them may be vital for your project's future. So the most important aspect of your customization effort is to make sure that you will be able to update your project to any future version. We would like to stress this again: being able to update your project to a new KVS version is the most important part of your project's future. Everything in the world is changed at a high pace. Here is an short list of what you would have lost if you didn't update your project since 3.9.1 (May 2017): [4.0.0, Jan 2018] Support for showing separate ads for separate device types: highly used feature to increase revenue. [4.0.0, Jan 2018] Faster preview trailers creation: saves much conversion time if you show video previews on mouse over. [4.0.0, Jan 2018] VAST support in player: the mostly used monetization method for video sites. [4.0.0, Jan 2018] Better tube grabbers with multi-threaded download support. [4.0.1, Feb 2018] Better adblock detection and monetization. [4.0.2, May 2018] Google ReCAPTCHA integration, GDPR compliance. [4.0.3, Sep 2018] Push notifications plugin: a new way of monetizing tube traffic with high earnings. [4.0.3, Sep 2018] VAST profiles: easy way to increase revenue from VAST. [4.0.4, Nov 2018] CRITICAL player bugfix for Android 9 devices. [5.0.0, Mar 2019] PHP7 support, animated GIF support in albums, huge performance optimizations. [5.1.0, Jul 2019] WebP support for video screenshots: easy way to reduce your traffic usage and get better google index. [5.1.0, Jul 2019] Player stats in KVS and Google. [5.1.0, Jul 2019] New CTR rotator concept. This list is just a 10% of what have been changed in KVS during the last 2 years! And we will keep going. Luckily, 95% of customizations will not prevent you from updating your KVS. With every new KVS version our main concern is to make update procedure as smooth as possible and keep all existing functionality to work as expected. KVS has its own integrity check, which can indicate if there are any custom changes in KVS system files. You can see this report in Plugins -> System audit as a part of installation check. Moreover you don't need to worry about them before update, as update plugin will do the same check and will report any system files that have custom changes and may be overridden by update procedure. If nothing is reported by KVS update plugin, you are good to go and continue updating your project without risk to break anything. Levels of customization in KVS There are 4 levels of KVS customization: 1) Theme / design customization. Theme in KVS is separated from KVS engine and thus any theme customization never affects your ability to update. This includes not only CSS styling change, but also changes in the following aspects of your site: Texts that are displayed. URLs and URL patterns that are used to generate URLs. Page parameters change (e.g. how many videos should be visible per page and so on). Any page can be restructured, built from scratch and fully re-designed without affecting your ability to update. This level is what KVS customization engine provides and this is what you are expected to be using. 90% of highly customized projects on KVS end up at this level and update to new KVS versions without any issues. 2) Legal customization options to add some custom functionality: At the moment KVS has the following APIs to customize some parts of its functionality: Ability to add custom video / album post-processing code via custom post-processing plugin. Ability to add custom grabbers extended from KvsGrabber base class (see How to create custom tube video grabber for KVS). Ability to add custom payment processors extended from KvsPaymentProcessor base class (see How to add custom payment processor in KVS 5.0). Ability to use {{query_kvs}} template function to run a custom query into KVS database (see Advanced theme customization: custom database queries). Ability to inject custom PHP logic into theme page generation via /admin/include/pre_xxx_page_code.php customization files. Ability to inject custom PHP logic into cron processing via /admin/include/cron_custom.php customization file. Ability to create custom Smarty functions and modifiers and use them to enhance display logic, if some complicated database selection logic is needed. Ability to add custom links to KVS admin panel menu. These options provide fair amount of possibilities to add customization logic with very low risk to affect further KVS updates. There is still some risk due to the following possible future scenarios: If your custom code executes queries into KVS database, it may be possible that database structure will change in future and your customization will start working incorrectly. It will not be fully broken however, as KVS update never changes database structure in incompatible way, making old SQL queries to become invalid. If your custom code relies on some other KVS common functions, they may be dropped with new versions without any notification. So better to customize without relying on anything specific in KVS. We may change API for custom grabbers and custom payment processors in future if we see the need to enhance it, however we will always consider the possible update implications as we usually do. KVS update plugin will require your attention if you have any custom these that may need manual changes. We will definitely change API for custom post-processing plugin at some point with prior notification during update and in what's new bulletin. 3) Semi-legal customization options to add some custom functionality: These are custom blocks that can further be used in theme development. Initially we honored using custom blocks as a legal customization option. However we noticed that this customization option was misused in 99% of cases. While designed to provide fully custom functionality that was not initially available in KVS, custom blocks were typically used to copy-paste from original blocks with minor custom enhancements, which were in most cases not necessary. This may work for some time, however in a long run it will result in your site not working correctly. How come? As a part of some huge changes we may completely revamp how some data is stored and how block operates it. The original block will be updated with KVS update, but your custom block, copied from the original long ago, will remain untouched and will use the old logic. Another common problem is that copy-pasted code in custom block uses many common KVS functions that are also changed from time to time. If we ever change function signature to a new one, or rename it to something else, your custom block and all pages that are using it will stop working after updating KVS. Some KVS customers were affected by this problem with custom blocks when we switched to PHP7 and changed all blocks to remove old PHP API. Unfortunately there is no way we can provide support for custom blocks and their coding, so many such customers were faced a dilemma: either they fully remove the custom functionality added by custom blocks, or they are not able to update to KVS 5 and above. And the more they wait, the more new features they are missing. As a result we strongly not recommend to use custom blocks in KVS, as this imposes high risk on your ability to update in some future. 4) Custom changes in KVS code Finally there is an option to put custom changes right into KVS system files and right in the needed place. This is the only customization option that needs open source code to be available. In some cases this is the only possible option to add the customization you need, however you should still consider updating your project by purchasing KVS update service from us. If the number of your customization changes is relatively small, we will not charge any additional fee, but things may differ if you have a huge amount of changes in many files. And we may also deny you in update if your customizations are conflicting with new KVS features / changes. IMPORTANT! If your developers tell that you need custom changes in KVS files, please make sure that they tried all possible options described in this post that do not need these changes before going forward. Some easy customization techniques - You need to display some data that can not be displayed via standard KVS blocks. Consider using custom database query {{query_kvs}} function (see Advanced theme customization: custom database queries). If you need complicated query syntax, consider adding custom smarty function similar to {{query_kvs}}. What you need is to simply put your function PHP file into /admin/smarty/plugins directory. You can duplicate function.query_kvs.php file and implement similar code with your custom query logic. - You need to do some display formatting that you can only do via PHP. Consider using Smarty syntax for your formatting. Smarty is very powerful and in many cases it can fully replicate any PHP logic. For example you can use PHP functions as modifiers in Smarty (in this case modifier should be put after the first parameter value of the function and all other parameters should be provided after). For example, if you want to use this PHP function: mt_rand(1, 10); You can do like this in Smarty: {{assign var="random" value=1|mt_rand:10}} {{if $random <= 2}} show this {{else}} show that {{/if}} If you want to iterate on some fixed list of items: $array = array_map('trim', explode(',', 'test1, test2, test3')); Same logic in Smarty: {{assign var="array" value=","|explode:"test1, test2, test3"}} {{foreach from=$array item="item"}} {{assign var="item" value=$item|trim}} {{if $item == 'test1'}}1{{elseif $item == 'test2'}}2{{elseif $item == 'test3'}}3{{/if}} {{/foreach}} Alternatively you can create custom Smarty function or modifier to code your logic in PHP if you don't want to cope with Smarty syntax. - You need some post-processing logic on some event. This is the most tricky one where developers typically insist that they need to add customizations to KVS code. Luckily they don't! The idea is that you could use custom cron script to monitor KVS database for some events or data items and do some post-processing offline. Here is how you can do it. For example you want to send some emails on new videos being approved, or add some internal message notification when somebody comments a video. First of all you need to add a new database column in the table, which data you will monitor. For example add custom_flag_notified tinyint(1) column to ktvs_comments table. Then your cron logic should query all comments that are approved and have custom_flag_notified=0. You may need to join with ktvs_videos table to find out user_id of the owner of each video being commented. Iterate over the selected list and create internal message for each owner about their video being comment. Also set custom_flag_notified=1 for each processed comment, so that they are not selected next time. Here you go: customization added, not a simple change in KVS system files.
  11. KVS theme customization engine provides a variety of ways to customize theme design and layout, but in some cases it is limited to sets of data it can select. The most important limitation is that you can't use blocks inside other blocks. There are 2 workarounds for doing this: adjust page layout structure (will not work in all cases) and pass block HTML into another block. Passing HTML from one block into another Let's take a look at the 2nd option, as it provides much more flexibility. Imagine that you have a list of videos and you want to render a drop down with categories there for fast navigation or to allow AJAX filtering on the selected categories. List of videos can be rendered only by list_videos block, which does not provide you list of categories to show a drop down inside. From the other hand list of categories can be rendered by list_categories block, however as it was said before you can't use one block inside another. For example this is how you insert list_videos on your page: {{insert name="getBlock" block_id="list_videos" block_name="Index Videos"}} In order to build category drop down and pass it to this list_videos block you should add list_categories block before it and assign its HTML result into a variable and then pass this variable to list_videos block using special var_ prefix: {{insert name="getBlock" block_id="list_categories" block_name="Categories Drop Down" assign="categories_dropdown_html"}} {{insert name="getBlock" block_id="list_videos" block_name="Index Videos" var_categories_dropdown_html=$categories_dropdown_html|smarty:nodefaults}} We added assign="categories_dropdown_html" to list_categories block in order to prevent it from printing its HTML into the place where it is inserted (since we need this HTML inside list_videos block). Then we passed $categories_dropdown_html into list_videos: var_categories_dropdown_html=$categories_dropdown_ html|smarty:nodefaults. Finally inside list_videos block template we can now display this HTML generated by list_categories block: {{$var_categories_dropdown_html|smarty:nodefaults}} This will print HTML code from list_categories block inside list_videos block. Looks like what we need. But in fact this will not work with AJAX pagination and other block-level AJAX functionality. So this approach can only be used with non-AJAX navigation. Using custom database queries from within blocks For advanced customization you may need to submit additional queries into database in order to select additional data, which is not selected by default in KVS. This will give you almost unlimited design customization abilities and at the same time will keep all KVS site engine benefits such as caching and high performance. This is not intended for basic users, in order to use it you should understand KVS database structure (since we do not provide any documentation for that, you can ask support if you have specific needs). You can use this in any cases where you want to display some data which is not provided by KVS. Here is the basic syntax: {{query_kvs select="list|single|count" table="table_name" [assign="var_name"] [sort_by="sort_by expression"] [limit="number"] [where_xxx="filter value"]}} Supported options: select: required, one of the following: list (to select a list of items), single (to select a single item), count (to select a number of items in database) table: required, table name to select from, should be specified in the following syntax: table="categories" assign: required if you select list or single item, because you need to put it into a variable, but not required if selecting count; if omitted the output will be printed right a way sort_by: optional, sorting SQL expression, should be a valid SQL expression for the specified table, example: sort_by="title asc" limit: optional, specify the number of rows you want to select, if not specified then all rows will be selected You can also specify list of filters using 3 types of where parameters: where_xxx: you should replace xxx with column name and specify value for "equals" match, for example: where_is_private="1" (will result in selecting only rows where is_private field = 1) wheregt_xxx: you should replace xxx with column name and specify value for "greater than or equals" match, for example: wheregt_post_date="2017-05-23" (will result in selecting only rows where post_date field >= "2017-05-23") wherelt_xxx: you should replace xxx with column name and specify value for "less than or equals" match, for example: wherelt_post_date="2017-05-23" (will result in selecting only rows where post_date field <= "2017-05-23") wherelike_xxx: you should replace xxx with column name and specify value for LIKE match, for example: wherelike_title="test%" (will result in selecting only rows where title field starts with "test") whereregexp_xxx: you should replace xxx with column name and specify value for regular expression match, for example: whereregexp_title="test.*" (will result in selecting only rows where title field matches regular expression "test.*") NOTE: where filters are protected from SQL injection, so you can safely use values passed from HTTP variables there, e.g.: where_is_private=$smarty.get.is_private, but sort_by expression is not protected, so you cannot use dynamic sorting here. Selecting data, which may have limited visibility, such as videos, albums, posts and playlists, will force default filtering (for example only active videos will be selected and etc). If for any specific reason you need to disable this filtering you should add default_filtering="false" option. You should be aware that submitting heavy selects may drop your project's performance. If used inside list_xxx blocks they can be cached pretty well and will not affect much. If used inside xxx_view blocks or outside blocks (at page level) their performance impact will be as much as many users log into your memberzone. Examples Take a look at these simple examples to see how powerful this feature can be for building an outstanding site design with very few lines. 1. Show a drop down with all categories in list_videos block: {{query_kvs select="list" table="categories" assign="list_categories" sort_by="title asc"}} <ul> {{foreach from=$list_categories item="category"}} <li>{{$category.title}}</li> {{/foreach}} </ul> Note that we use assign="list_categories" here and then render this list using standard Smarty {{foreach}}. 2. Show the number of videos added for the last year (last 365 days): {{assign var="where_post_date" value=$smarty.now-365*86400|date_format:"%Y-%m-%d"}} Videos added last year: {{query_kvs select="count" table="videos" wheregt_post_date=$where_post_date}} Note that we first form a $where_post_date variable in the needed date format and then pass it to query using wheregt_post_date=$where_post_date. Also we are not using assign with query function, so it will print result right a way: Videos added last year: 1276 3. Show the number of videos that are in process now, e.g. processing queue: Videos processing right now: {{query_kvs select="count" table="videos" where_status_id="3" default_filtering="false"}} Note that here we disabled default filtering via default_filtering="false" since by default videos that are in process status will be filtered out from the query. Using this query can be useful if you want to prevent users from uploading content when there are too many videos being processed. For example you can add this code to video_edit block, which renders upload form: {{query_kvs select="count" table="videos" where_status_id="3" default_filtering="false" assign="videos_processed_now"}} {{if $video_processed_now>100}} Sorry, the upload is limited as we have too many videos in process right now. Please come back later! {{else}} ... show upload form as usual {{/if}} Note that here we are using assign="videos_processed_now" to assign the count of currently processed videos into a variable, so that we can further use it in IF statement. 4. In model view page show how many other models with the same hair color: {{if $data.hair_id>0}} More models with the same hair color: {{query_kvs select="count" table="models" where_hair_id=$data.hair_id}} {{/if}} Note here we first check if hair color is specified for this model and then show count of models with the same hair color, we stick to the current model's hair color with using where_hair_id=$data.hair_id.
  • Create New...