Skip to content
View in the app

A better way to browse. Learn more.

JimiWikman.se

A full-screen app on your home screen with push notifications, badges and more.

To install this app on iOS and iPadOS
  1. Tap the Share icon in Safari
  2. Scroll the menu and tap Add to Home Screen.
  3. Tap Add in the top-right corner.
To install this app on Android
  1. Tap the 3-dot menu (⋮) in the top-right corner of the browser.
  2. Tap Add to Home screen or Install app.
  3. Confirm by tapping Install.

User Story Mapping - a great tool for business analysis

By 💫 Jimi Wikman ·
User Story Mapping is something that you probably have heard about if you work as product owner, business analyst or requirement analyst. It is a great tool for quickly break down customer journeys into system areas to map out work. The trick however is to use it where it is useful, which is in the business process when working with business analysis.
I often see people presenting User Story Mapping as a requirement tool, or even something that is useful for development as a backlog tool. This is usually suggested by business analysts and managers such as product owners, which makes sense because it is for them User Story Mapping is actually useful. Unfortunately it makes no sense for a developer since code are not following a customer journey.

For development, it is often very difficult to map things into a User Story Map. This is especially difficult for backend development where a lot of the work never even is seen by the end user. This causes some issues, not because User Story Mapping is bad in any way, but because it is defined on a user story level, when it is actually a process above the user story level.
For me this process is best used on the business side to map out features by the product owner and the Business Analyst. This is where the User Story Map truly shines, making it easy to understand where in the customer journey certain features live in a visual way. That is not to say that it has no value to a developer, quite the opposite. It is very useful to understand what feature you are working on and where it fit in the flow of things.
It is just not what is important for the development itself.
When it comes to the development itself you still want to have well-defined user stories in the form of work orders and acceptance criteria. Each user story also need to be small enough to be possible to complete within one increment (one sprint) and in most cases you will find that the user stories presented in the User Story Map are way too big for that.
I would actually suggest to not use the term user story mapping since that to me is misleading. I would instead call it User Feature Mapping to avoid confusion.
 
Try it!
You can use this purely analog by mapping up a customer journey on a white board and then use that with stickies, or you can take advantage of apps in Jira for example. My two favorites are Easy Agile User Story Maps for Jira by Easy Agile and Agile User Story Mapping Board for Jira by DevSamurai. Both are great tools that will give you the tools you need to get started with User Story Mapping.
 
 
  • 0
  • 1790

Developer Velocity Index - one-sided nonsense or useful?

By 💫 Jimi Wikman ·
Developer Velocity Index, or DVI for short, is pushed hard by Microsoft right now as a way to sell Azure DevOps as I see it. So what is it and is it just another pointless measurement tool that does not address the big elephant in the room, or can it actually be useful? Let us dig into it and find out.
So Developer Velocity Index is a tool for measuring, well, quite a lot actually. At first glance we see a lot of focus on tools, which of course is the main goal for Microsoft as they need to get more clients for Azure Devops that is trying to challenge prominent actors such as Atlassian.
If you look a bit deeper however you will see that the DVI is pretty extensive. While focus is on tools it seems to look at these from a perspective that is not just focused on the development discipline. DVI claim to involve 46 different drivers across 13 dimensions and that is pretty good. I say claim because I have not tried this yet, so I do not know to what extent this is actually true.
 

 
The DVI is based on self-assessment through questionnaires, which is not a bad way to do this honestly. It will ensure that the introverts also get a say, which is not always the case in verbal situations where the extroverts are always loudest.
I see the tool angle a lot when reading about DVI, but when you dig down you see that what that almost always mean is that the tools in question bridge the gap between business and IT. Tools that help manage inflow, portfolio management and of course tools to help with clarifying need, especially in Agile teams. I think Tibi Covaci from Microsoft express this best:
I think this is profoundly true. Like my former colleague Eva Nordstrom would say "A fool with a tool is still a fool".  Good tools with a good and well-educated organization however, that will truly generate magic. It is my hope that DVI can help illustrate the need for organizational change to help facilitate that. This is often the biggest issue in my experience and one that is very hard to overcome.
It is also no big revelation that most organizations find the funnel between business and IT to be lacking or that this is where most organizations fail. The introduction of Agile often make this worse, which is not the fault of Agile, but the way it is implemented in organizations. Hopefully DVI can illustrate the need to have a proper portfolio management on the operative level and that even in Agile work teams you need to ensure that demands are evolved.
Ad-Hoc and shooting things from the hip are sure ways to make any developer sad after all, and we all want some form of structure to our chaos to ensure we know what to do, yet remain flexible...
Developer Velocity Index is interesting, but it is still a stick that should not be needed in mature organizations. Sadly there are very few mature organizations out there, so I think this is very interesting for many reasons. I will dig into it some more and see what I can learn.
What do you think?
Is it just a selling tool for more tools you don't need, or something that can drive actual change?
  • 1
  • 2186

Ubiquiti hacked - the extent of the breach is still unclear

By +Kryptera.se ·
Yesterday I received an email that the American company Ubiquiti has been hacked. Ubiquiti is i.a. one of the world's largest manufacturers of base devices for WiFi communication. The email contains relatively little information because the company states that they do not know the extent yet.
Although it has been a long time since I myself used Ubiquiti's cloud service, I assume that it is entirely possible to gain access to the local network via Ubiquiti's central service, hence this is extra serious. I can also imagine that DNS can be reconfigured, firmware can be changed, etc.
What appears in the email is that the username, hashed password, address and telephone number may have been leaked. It also appears that this is a third-party supplier where the leak must have taken place.
The mailing has also been confirmed by Ubiquiti themselves, see forum thread here (via the Security Bubble). The mailing went via Mailchimp and used i.a. tracking links, which made it initially difficult to determine the authenticity of the email.
 

The Daily Stand-up - can we make it better?

By 💫 Jimi Wikman ·
If you have worked in IT in the past 10-15 years or so, you probably have endured the endless regurgitation of meaningless information in a daily stand-up. You have probably felt the anxiety of being judged and been annoyed over your teammates doctoring their results to look more productive. You probably also wished you did not have to go to the meetings once or twice. What if I told you there is a better way to manage daily stand-ups? Because there is.
First let us figure out what the purpose of a daily stand-up is and where it comes from. The need to organize groups and to collaborate is as old as time itself and while many consider the daily stand-up, or daily scrum to originate from the Scrum framework, that is not true. Regular meetings have been a common practice since long before Scrum, and it is also why it is the most used aspect of Scrum in less than Agile work processes.
What Scrum did however was to add psychological stress to the formula with the intent of increasing productivity. This was done by putting emphasis on proof of progress rather than collaboration. It is an effective way to shame people into becoming more productive, and it is a typical behavior for extroverts to seek the admiration and praise of others. The downside is increased levels of stress, which is counterproductive. In many cases it also leads to manipulation of data and fragmentation of work, especially in continuous delivery situations where people tend to work on multiple things at the same time.
For many years Scrum had three questions that was the law to regurgitate every daily meeting:
What did I do yesterday that helped the development team meet the sprint goal? What will I do today to help the development team meet the sprint goal? Do I see any impediment that prevents me or the development team from meeting the sprint goal? In the 2020 scrum guide they have backed off from this a bit, but they still focus on the feeling of guilt by pushing the conversation towards progress.
This is even more emphasized in the statement of the benefits of the daily stand-up:
Again this is clearly written from the perspective of an extrovert because for many introverts this is NOT a forum for improved communication. The idea that the daily stand-up eliminate the need for other meetings is not true. It most certainly can consolidate questions into a more focused forum, which does reduce the need to bother developers and others multiple times, but you often find reasons for more meetings, not less.
How do we make this better?
Let us first decide why we want to have daily meetings in the first place. The most obvious reason is to gain control. This is how most managers that are detached from the team see the daily stand-up because it is the only way they can stay on top of things, so they can look good in other meetings. That is not what daily stand-ups are for however and as a manger you should instead manage your time and make sure you work with the team and not act as a proxy.
The reason we have daily stand-up should be to make sure everyone in the team have a voice and to make sure everyone is informed. By making sure everyone in the team get a voice we can find impediments and help each other solve problems. We can lift concerns and ask questions in a safe setting that allow the team to feel safe. It allows for a common forum for information, so everyone can feel that they have the information they need at all times.
We do this because questions and doubt are the bane of productivity and team health.
The first thing we do is to remove the time cap. Our minds are very sensitive to time and performance under pressure, so we remove that. Instead, we add a 30-minute slot every morning where we spend as much time as we need. Sometimes it is just 5 minutes, sometimes we extend the 30 minutes and change it into another type of meeting.
The second thing we do is remove the need to report what has been done. This is the cause of much anxiety and in a healthy team you should not need that type of information anyway. Instead, we focus on questions, impediments and requests for help. We make sure everyone in the team get a chance to speak, and we make sure that everyone feel safe, so they dare to say what is on their mind. This is important because one thing we want to capture is if time estimates need to be extended or if new tasks might be needed.
We sort things that come up as team related or personal, so we can focus on the team related issues first and then take personal questions after the meeting to make the disruption as small as possible. To make this disruption even smaller we put the daily meeting as early as possible without limiting peoples freedom. This means that you most likely have people in the team that have kids or other family situations that require them to have some flexibility in the morning and afternoon. 9 or 10 are common times, but you can just as well do this after lunch for example. The aim is to avoid disrupting the team as content switching is bad.
The final part is for the team lead and architect to inform the team of things happening outside the team that is relevant to the team.
I strongly suggest that you make this daily meeting as comfortable as possible instead of actually making it an actual stand-up. The reason for that is that most people are more likely to raise questions and ask for help when they are comfortable. If you can align this around a coffee break or breakfast, then that is excellent.
The summary:
Schedule the meeting as early as possible without limiting the flexibility of your team members. Go around the team and let everyone ask questions, request help and raise concerns if there are any. Team lead and Architect give information relevant for the team. Confirm activities such as additional meetings or request for information or to solve impediments Grab some water or coffee and then return to work again. I find that these types of daily meetings often lead to better productivity for the simple reason that people feel less stressed when they get information and can get their problems solved. The outcome of these meetings often lead to technical discussions to answer technical questions and sometimes new activities to refactor things that might not have been captured otherwise.
So if you are stuck in a daily routine of regurgitating Jira numbers where you feel the need to adjust numbers a bit just to look good, then I suggest you give this approach a try instead. 
Management by fear and intimidation is a poor substitute for making your team feel safe and informed.
  • 0
  • 1631

Advanced Roadmaps - Adding a portfolio workflow to Jira Software

By 💫 Jimi Wikman ·
Regardless if you work in a continuous delivery flow like Kanban, a sequential delivery flow like waterfall or an iterative delivery flow like Scrum, you will benefit greatly from having portfolio management. Having a dedicated funnel ensures that you get a proper overlook of your incoming request. In this article we will discuss how that can be done by creating a process for it in Jira Software.
Advanced Roadmaps is a very useful tool because it allows us to get the big picture. It also allows us to scale the hierarchies in Jira for issue types. This is important because the standard hierarchies are designed for teams, so they do not scale well. While you can work around this using the sideways approach where you have the higher hierarchies in a separate project and link between them, that is not really a hierarchy. This also works poorly when you wish to display things in Advanced Roadmap.
The first thing we need to do for a proper hierarchy view in Advanced Roadmaps is to define the portfolio levels. We need to accommodate for multiple scenarios as requests, or demands, often come in different forms. The most common types are projects or even programs, or new features. We also want to make sure we can work with the different frameworks out there, so we add a collaboration level as well as defined in SAFe.
To avoid a lot of confusion between different frameworks and other tools, we rename the Epic issue type in Jira Software to Feature. It will not be perfect, but it will have to do until we get the ability to rename it properly when Atlassian makes that change in the future.  We do this by going to Issues -> Issue Types and edit the Epic issue type. We also add two new issue types for Initiative and Capability.
Next we go to Plans -> Settings -> Advanced Roadmaps hierarchy configuration. There we add two new levels called Initiative and Capability and map them with our newly created issue types with the same names. Finally, we rename the Epic to Feature, and we should have our first hierarchy defined. We can go back and adjust this later if we want. The setup should now look like this:

We can now add this to our projects by adding the issue types to our Issue Type Schemas. Depending on your setup you may want the new issue types for Initiative and Capability to exist in a separate Issue Type Schema, or you will add them to the standard schema. Either way we still need to give the new issue type a workflow that match their purpose. So we head over to Issues -> Workflows and create a new workflow.
Again we want to consider what the purpose of these issue types is and who will actually work with them. Initiative, Capability and Feature are all used by management and requirement analysts to provide information on how to realize demands. That means that we do not have the same transfer of responsibility as for User Stories. Instead, we need to consider what we actually do in the portfolio and what we need to track in terms of activities.
SAFe have a pretty good example of this for their Portfolio Kanban. Their first step is Funnel and for us that is pretty much everything that comes into our workflow, and we define this as new. Anything that comes in to the portfolio needs to be reviewed to see what we should focus on next. Since we will not review everything we add two steps for the review process: Ready for Review and In Review. This allows us to only pick things that we want to review by moving them from new to Ready for Review, and then we have an active status to indicate that we are working on this.
We do the same for Analysis, which is the step where we involve Business Analysts and Requirement Analysts to define the need in more detail. For this we add Ready for Analysis for when the Review process lead to us spending time to break down the need in a requirement process. We also add the In Analysis to indicate that it has entered the Requirement process.
Before we close this workflow we add the Implementation process as well. Ready for Implementation indicate that the requirement process has led to a decision to implement and In Implementation indicate that the need is being fulfilled. We do not fracture this further as we can easily drill down to individual tasks in Advanced Roadmaps, and it is not really useful to have for example test and release part of this workflow. That is because each issue type will have many issue types below it, and you can only transition on the last sub-task, making it misleading and feeling stagnant.
Finally, we add a status for Blocked/Pending to allow us to track when things are preventing this activity from being fulfilled. This can be external or internal dependencies or financial reasons for example.
As always we go for an open workflow to allow for free transitions. We add a close screen to the Close status to set a resolution and then add a post function to remove resolution on all other statuses. This workflow should now look like this:

The final step is now to go to our workflow scheme and add this new workflow. We do this by going to Issues -> Workflow Schemes and click edit on the workflows scheme we have Initiative, Capability and Feature added to. We click add Workflow and add the new workflow we just created. Then we select that this workflow will be applied to Initiative, Capability and Feature before hitting save.
This should now allow you to work inside Advanced Roadmaps using Initiatives, Capabilities and Features in a proper portfolio management process that allow for larger projects and programs as well as manage smaller feature or even user stories.
I hope you found this useful and as always, if you have any questions just write a comment and I will do my best to help you.

Building a Movies database - using plugin and some styling

By 💫 Jimi Wikman ·
This week I took a look at a plugin I had and decided to style it a bit to fit the image I had in my mind. As it turned out this was very easy and the only thing that stopped me for a while was how to use custom fields. Once that was done it was smooth sailing and quite fun to customize. This is how I did it, so you can do it yourself if you want.
The app that we will be working with is the Movies & TV Shows by Adriano Faria. This is an amazing app that use the TMDB database to fetch data for the movies and TV shows, making adding movies and TV shows fast and fun. Adriano has several similar apps and I also have the Books version that I probably look at later as well.
In order to modify the app we need to go to the themes (customization -> Apperance -> Themes) and click the Edit HTML & CSS button. This takes us to the templates for our theme. There you will have two tabs, one for templates and one for CSS. In both of those tabs we will have a section called "Movies". This is where we find the app templates and the CSS that the app use.

 
Inside the templates we will focus on a few templates to make the design that we want to make.
View -> View: This is the template that control the layout for when you look at a movie or tv show. Index -> indexBlock: This is the template that control the latest and random items block on the start page. Index -> featuredMovie: This is the template that control the featured movies block on the start page. front -> browse -> rows: This is the template that control the listing inside a category. We will also work with the CSS that is located in the CSS tab:
movies -> front -> movies.css  
Changing the view
The first thing I did was to change the View template. I had the idea that I wanted to have a big background image behind the content. This is a bit tricky since the content is trapped inside a div that have a fixed width. The second thing I wanted was to add more data and I especially wanted to add a custom rating icon for my rating since I will use this as my recommendation site of sorts.
So first I needed to figure out how to add custom fields and decide what fields I wanted. I decided on the following fields:
Official Trailer (ID:8) - The ID of the trailer, so I can add it to an embedded video player Certification (ID:6) - Just a text field to add the certification. TV Year (ID:4)  - Just a text field here as well. My Review (ID:2) - An editor field, so I can write my review. My Rating (ID:5) - Just a number field, so I can add values between 1 and 100. Backdrop (ID:1) - An upload field, so I can add a big image behind the content. In order to show these fields I need to use this little piece of code:
{{$fields = $movie->customFields();}}{$fields['field_1']} I then just change the field_1 to the field I want to display. You can see what the id of your field is by hovering over the edit button in the custom fields view.
I also want to be able to add some conditions, so I only see things if the field is filled in and in order to do that I use this:
{{if $movie->customFields('field_1')}} With that we can now add our custom fields to the template and start looking at how to style them.
 
The Backdrop
The backdrop is shown by displaying the file that we uploaded, but it will be constrained within the content and we want to break it out from that. This is where we use a little hack in CSS and I added a class to the div surrounding the backdrop and then added this CSS to it:
.full-width { width: 100vw; position: relative; left: 50%; right: 50%; margin-left: -50vw; margin-right: -50vw; margin-top:-15px; min-height:800px; background-image:url(https:{media="528"}); background-color: #294459; background-position:top center; background-repeat:no-repeat; background-size:cover; } This now show the image in full width and I have this idea that I want to add a play button to the backdrop that show a trailer when I click it. There are several ways to do this, but I decided to use the built-in function in Invision Community called ips.ui.dialog. This nifty function allow me to open a modal and either populate it with a link or content inside the same page. As it does not work to refer directly to the url of the trailer, we use the embed code instead and place the content in a hidden div in the document.
{{if $movie->customFields('field_1')}} <div class="full-width" style="background-image:url('https://jimiwikman.se/uploads/{{$fields = $movie->customFields();}}{$fields['field_1']}');"> <!-- Official Trailer --> {{if $fields['field_8']}} <a href='#dialogContent' data-ipsDialog data-ipsDialog-size="fullscreen" data-ipsDialog-content='#dialogContent'> <div class="JWSE_play"> <span class="JWSE_playBtn"><i class="far fa-play-circle"></i></span> <span class="JWSE_playTxt">Play Trailer</span> </div> </a> <div id="dialogContent" class="JWSE_video-container ipsHide"> <iframe src="https://www.youtube.com/embed/{$fields['field_8']}" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe> </div> {{endif}} In the code I refer to the custom field I created for the official trailer that hold the movie ID. I also wrap the code in an if query, so it only shows if a value is entered. I use FontAwesome to show the play icon and I define the modal to be full view using the data-ipsDialog-size attribute. Currently, narrow, medium, wide and full screen are supported as values for this option.
I then style this with the following CSS:
/*** Youtube Popup ***/ .JWSE_video-container { overflow: hidden; position: relative; width:100%; } .JWSE_video-container::after { padding-top: 56.25%; display: block; content: ''; } .JWSE_video-container iframe { position: absolute; top: 0; left: 0; width: 100%; height: 100%; } .JWSE_play { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); text-align:center; height:170px; width:170px; background: rgb(41,68,89, 0.5); display:block; border-radius:100% } .JWSE_playBtn{ position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); font-size:70px; opacity:0.9; color:white; display:block; } .JWSE_playTxt{ position: absolute; top: 80%; left: 50%; transform: translate(-50%, -50%); font-size:16px; color:white; display:block; }  
My Rating
Adding a rating icon to make the rating number look cool is next, and I wanted a circle with numbers inside. This can be done in any number of ways, but I decided on a fairly simple version of a SVG circle using css keyframes for animation.
<!-- JWSE Rating Circle --> {{if $fields['field_5']}} <div class="jwse_ratingCircle"> <svg viewBox="0 0 36 36" class="circular-chart orange"> <path class="circle-bg" d="M18 2.0845 a 15.9155 15.9155 0 0 1 0 31.831 a 15.9155 15.9155 0 0 1 0 -31.831" /> <path class="circle" stroke-dasharray="{{$fields = $movie->customFields();}}{$fields['field_5']|raw}, 100" d="M18 2.0845 a 15.9155 15.9155 0 0 1 0 31.831 a 15.9155 15.9155 0 0 1 0 -31.831" /> <text x="17" y="22.35" class="percentage">{{$fields = $movie->customFields();}}{$fields['field_5']|raw}</text> <text x="27" y="15.35" class="percentageSmall">%</text> </svg> </div> {{elseif $fields['field_5'] == 0}} <div class="jwse_ratingCircle"> <svg viewBox="0 0 36 36" class="circular-chart"> <path class="circle-bg" d="M18 2.0845 a 15.9155 15.9155 0 0 1 0 31.831 a 15.9155 15.9155 0 0 1 0 -31.831" /> <path class="circle" stroke-dasharray="0, 100" d="M18 2.0845 a 15.9155 15.9155 0 0 1 0 31.831 a 15.9155 15.9155 0 0 1 0 -31.831" /> <text x="18" y="19.35" class="percentage unrated">Unrated</text> </svg> </div> {{endif}} As you can see in the code I have two versions of the circle, one for when I have added a value and one for when I have not. I had to add a condition to the second version because as I have a number field it will always be 0, which means that it is never empty. This means that the first condition will always be true, even if I have not added anything to the field.
To this I added the following CSS:
 
/*** My Rating Circle ***/ .jwse_MyReviewText{ position:relative; } .jwse_ratingCircle { float: right; position: absolute; top: 0px; right: 0px; z-index:999; width:100px; } .jwse_ratingCircle-indexBlock { float: right; position: absolute; top: 0px; right: 0px; z-index:999; width:50px; } .jwse_ratingCircle-categoryList { float: left; position: absolute; top: -5px; left: 112px; z-index:999; width:50px; } .circular-chart{ display: block; margin: 10px auto; max-width: 80%; max-height: 100px; background-color: var(--color-baseblue); border-radius: 50%; } .circular-chart-indexBlock{ max-width:100%; max-height:50px; top:-6px; right: 3px; position:relative; } .circular-chart-Featured{ max-height:50px; top:-5px; right: 17px; position:relative; } .circular-chart_unrated{ background-color: var(--color-palegrey); } .circle-bg { fill: none; stroke: #eee; stroke-width: 3.8; opacity: 0.3; } .circle { fill: none; stroke-width: 3.8; stroke-linecap: round; animation: progress 1s ease-out forwards; } @keyframes progress { 0% { stroke-dasharray: 0 100; } } .circular-chart.orange .circle { stroke: var(--color-jwsecoral); } .percentage { fill: white; font-family: Montserrat; font-size: 13px; text-anchor: middle; } .percentageSmall{ fill: white; font-family: Montserrat; font-size: 5px; text-anchor: middle; } .unrated{ font-size:5px; } As you can see I have variants for the indexBlock and featuredBlock as well as adustment for the category list. This because the images are different for those areas and we need to adjust a bit.
 
Let's put it all together
With this I now have all the custom fields shown on the page, a backdrop that cover the full width of the page and I have a visual representation of the rating. All that is left is to put things together in the templates and add styles to it. We also need to adjust a bit for mobile view.
With permission from Adriano, I will show you the code I use, and you can copy and paste it into your own movies theme files to get the layout I use here on this site. I have not cleaned these up, so there are some areas that need cleaning, but it works.
 
View Rows indexBlock featuredMovie CSS <div class="ipsRichEmbed" style="max-width: 500px; border: 1px solid rgba(0,0,0,0.1); "> <div class="ipsRichEmbed_masthead ipsRichEmbed_mastheadBg ipsType_center"> <a href="https://www.vowel.com/" rel="external nofollow" style="background-image: url( 'https://s3.amazonaws.com/com.vowel.resources/social/vowel-share-thumb.jpg' ); background-position: center; background-repeat: no-repeat; background-size: cover; height: 120px; display: block;"><img alt="vowel-share-thumb.jpg" class="ipsHide" data-loaded="true" data-src="https://s3.amazonaws.com/com.vowel.resources/social/vowel-share-thumb.jpg" src="https://jimiwikman.se/applications/core/interface/js/spacer.png" style="height: auto;"></a> </div> <div style="padding: 10px;"> <h3 class="ipsRichEmbed_itemTitle ipsTruncate ipsTruncate_line ipsType_blendLinks"> <a href="https://www.vowel.com/" rel="external nofollow" style="text-decoration: none; margin-bottom: 5px;" title="Home - Vowel">Home - Vowel</a> </h3> <div class="ipsType_light"> WWW.VOWEL.COM </div> <hr class="ipsHr"> <div class="ipsSpacer_top ipsSpacer_half" data-ipstruncate="" data-ipstruncate-size="3 lines" data-ipstruncate-type="remove" style="overflow-wrap: break-word;"> <span>Vowel is a tool that captures your team&rsquo;s meetings. Use it to annotate meetings in real-time, tag action items, call back ideas verbatim&mdash;and a whole lot more.</span> </div> </div> </div> <div class="ipsTabs ipsTabs_contained ipsTabs_withIcons ipsTabs_large ipsTabs_stretch ipsClearfix" data-ipstabbar="" data-ipstabbar-contentarea="#" data-ipstabbar-defaulttab="1" data-ipstabbar-updateurl="false" id="elTabBar"> <ul role="tablist"> <li> <a aria-selected="true" class="ipsType_center ipsTabs_item ipsTabs_activeItem" href="" id="1" rel="" role="tab"> View </a> </li> <li> <a class="ipsType_center ipsTabs_item" href="" id="2" rel="" role="tab"> Rows </a> </li> <li> <a class="ipsType_center ipsTabs_item" href="" id="3" rel="" role="tab"> indexBlock </a> </li> <li> <a class="ipsType_center ipsTabs_item" href="" id="4" rel="" role="tab"> featuredMovie </a> </li> <li> <a class="ipsType_center ipsTabs_item" href="" id="5" rel="" role="tab"> CSS </a> </li> </ul> </div> <section class="ipsTabs_panels ipsTabs_contained"> <div class="ipsTabs_panel" id="ipsTabs_elTabBar_1_panel"> <pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_9425_9" style=""> {{if $club = $movie->container()->club()}} {{if settings.clubs and settings.clubs_header == 'full'}} {template="header" app="core" group="clubs" params="$club, $movie->container()"} {{endif}} <div id='elClubContainer'> {{endif}} {{$fields = $movie->customFields();}} {{if $fields['field_1']}} <div class="full-width" style="background-image:url('https://jimiwikman.se/uploads/{$fields['field_1']|raw}');"> <!-- Official Trailer --> {{if $fields['field_8']}} <a href='#dialogContent' data-ipsDialog data-ipsDialog-size="fullscreen" data-ipsDialog-content='#dialogContent'> <div class="JWSE_play"> <span class="JWSE_playBtn"><i class="far fa-play-circle"></i></span> <span class="JWSE_playTxt">Play Trailer</span> </div> </a> <div id="dialogContent" class="JWSE_video-container ipsHide"> <iframe src="https://www.youtube.com/embed/{$fields['field_8']}" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe> </div> {{endif}} </div> {{else}} <div class="full-width" style="background-image:url('{media="528"}')"></div> {{endif}} <div class=""> <div class="ipsPageHeader ipsClearfix ipsSpacer_bottom"> {template="contentItemMessages" group="global" app="core" params="$movie->getMessages(), $movie"} <div class='ipsBox ipsResponsive_pull'> <div class='ipsColumns ipsColumns_collapsePhone'> <aside class='ipsColumn ipsColumn_veryWide ipsPadding_right:none'> <div class='ipsPad lg:ipsPos_sticky'> <div> {{if $movie->cover_thumb}} <div class='ipsPos_center'> <div class="sosMovieCover"> <!-- JWSE Rating Circle --> {{if $fields['field_5']}} <div class="jwse_ratingCircle"> <svg viewBox="0 0 36 36" class="circular-chart orange"> <path class="circle-bg" d="M18 2.0845 a 15.9155 15.9155 0 0 1 0 31.831 a 15.9155 15.9155 0 0 1 0 -31.831" /> <path class="circle" stroke-dasharray="{{$fields = $movie->customFields();}}{$fields['field_5']|raw}, 100" d="M18 2.0845 a 15.9155 15.9155 0 0 1 0 31.831 a 15.9155 15.9155 0 0 1 0 -31.831" /> <text x="17" y="22.35" class="percentage">{{$fields = $movie->customFields();}}{$fields['field_5']|raw}</text> <text x="27" y="15.35" class="percentageSmall">%</text> </svg> </div> {{elseif $fields['field_5'] == 0}} <div class="jwse_ratingCircle"> <svg viewBox="0 0 36 36" class="circular-chart"> <path class="circle-bg" d="M18 2.0845 a 15.9155 15.9155 0 0 1 0 31.831 a 15.9155 15.9155 0 0 1 0 -31.831" /> <path class="circle" stroke-dasharray="0, 100" d="M18 2.0845 a 15.9155 15.9155 0 0 1 0 31.831 a 15.9155 15.9155 0 0 1 0 -31.831" /> <text x="18" y="19.35" class="percentage unrated">Unrated</text> </svg> </div> {{endif}} {template="coverPhoto" group="global" app="movies" params="$movie->cover_thumb, '', $movie->title, 'large'"} </div> <div class="jwse_MoviePhoto_Footer"> {template="rating" group="global" location="front" app="core" params="'large', $movie->averageReviewRating(), \IPS\Settings::i()->reviews_rating_out_of, $movie->memberReviewRating()"}&nbsp;&nbsp; <span class='ipsType_normal ipsType_light'>({lang="num_reviews" pluralize="$movie->reviews"})</span> </div> </div> {{else}} <center><img src='{resource="movie.PNG" app="movies" location="front"}'></center> {{endif}} <hr class='ipsHr'> {{if $movie->imdb_id OR $movie->homepage OR $movie->wikipedia OR $movie->instagram OR $movie->facebook OR $movie->twitter OR $movie->justwatch}} <ul class='ipsList_inline ipsSpacer_both ipsType_center'> {{if $movie->homepage}} <li><a title="{lang="movies_homepage_visit"}" data-ipsTooltip target="_blank" href="{$movie->homepage}"> <i class="fa fa-link fa-2x" aria-hidden="true"></i></a></li> {{endif}} {{if $movie->imdb_id}} <li><a title="{lang="movies_imdb_page_visit"}" data-ipsTooltip target="_blank" href="https://www.imdb.com/title/{$movie->imdb_id}/"> <i class="fa fa-imdb fa-2x" aria-hidden="true"></i></a></li> {{endif}} {{if $movie->facebook}} <li><a title="{lang="movies_facebook_page"}" data-ipsTooltip target="_blank" href="{$movie->facebook}"> <i class="fa fa-facebook fa-2x" aria-hidden="true"></i></a></li> {{endif}} {{if $movie->instagram}} <li><a title="{lang="movies_instagram_page"}" data-ipsTooltip target="_blank" href="{$movie->instagram}"> <i class="fa fa-instagram fa-2x" aria-hidden="true"></i></a></li> {{endif}} {{if $movie->justwatch}} <li><a title="{lang="movies_justwatch_page"}" data-ipsTooltip target="_blank" href="{$movie->justwatch}"> <i class="fa fa-play fa-2x" aria-hidden="true"></i></a></li> {{endif}} {{if $movie->twitter}} <li><a title="{lang="movies_twitter_page"}" data-ipsTooltip target="_blank" href="{$movie->twitter}"> <i class="fa fa-twitter fa-2x" aria-hidden="true"></i></a></li> {{endif}} {{if $movie->wikipedia}} <li><a title="{lang="movies_wikipedia_page"}" data-ipsTooltip target="_blank" href="{$movie->wikipedia}"> <i class="fa fa-wikipedia-w fa-2x" aria-hidden="true"></i></a></li> {{endif}} {{if $movie->canEdit()}} <li> <a href="{$movie->url()->csrf()->setQueryString( array( 'do' => 'editUrls' ) )}" data-ipsDialog data-ipsDialog-size='medium' data-ipsDialog-title="{lang="movie_edit_urls"}"> <i data-ipsTooltip title="{lang='movie_edit_urls'}" class="fa fa-pencil fa-fw" aria-hidden="true"></i> </a> </li> {{endif}} </ul> <hr class='ipsHr'> {{endif}} <ul class="ipsDataList ipsDataList_reducedSpacing ipsSpacer_top"> <li class="ipsDataItem"> <span class="ipsDataItem_generic ipsDataItem_size3"><strong>{lang="movie_type"}</strong></span> <span class="ipsDataItem_generic">{lang="movie_type_{$movie->type}"}</span> </li> <li class="ipsDataItem"> <span class="ipsDataItem_generic ipsDataItem_size3"><strong>{lang="movie_status"}</strong></span> <span class="ipsDataItem_generic">{$movie->status}</span> </li> {{if $movie->type=='tv'}} <li class="ipsDataItem"> <span class="ipsDataItem_generic ipsDataItem_size3"><strong>{lang="movie_seasons"}</strong></span> <span class="ipsDataItem_generic">{$movie->seasons}</span> </li> <li class="ipsDataItem"> <span class="ipsDataItem_generic ipsDataItem_size3"><strong>{lang="movie_episodes"}</strong></span> <span class="ipsDataItem_generic">{$movie->episodes}</span> </li> {{endif}} {{if $movie->type=='movie'}} <li class="ipsDataItem"> <span class="ipsDataItem_generic ipsDataItem_size3"><strong>{lang="movie_original_language"}</strong></span> {{$language = \IPS\movies\Movie::getLanguageName( $movie->original_language );}} <span class="ipsDataItem_generic">{$language}</span> </li> {{if $movie->budget}} <li class="ipsDataItem"> <span class="ipsDataItem_generic ipsDataItem_size3"><strong>{lang="movie_budget"}</strong></span> <span class="ipsDataItem_generic">{expression="\IPS\Member::loggedIn()->language()->formatNumber( $movie->budget, 2 )"}</span> </li> {{endif}} {{if $movie->revenue}} <li class="ipsDataItem"> <span class="ipsDataItem_generic ipsDataItem_size3"><strong>{lang="movie_revenue"}</strong></span> <span class="ipsDataItem_generic">{expression="\IPS\Member::loggedIn()->language()->formatNumber( $movie->revenue, 2 )"}</span> </li> {{endif}} {{endif}} </ul> {{if $movie->topic()}} <hr class='ipsHr'> <a href='{$movie->topic()->url()}' title='{lang="movie_discussion_topic"}' class='ipsButton ipsButton_primary ipsButton_fullWidth'>{lang="movie_discussion_topic"}</a> {{endif}} <div class='ipsResponsive_showPhone ipsResponsive_block ipsSpacer_top'> {template="follow" app="core" group="global" params="'movies', 'movie', $movie->id, $movie->followersCount()"} </div> </div> </div> </aside> <article class='ipsColumn ipsColumn_fluid ipsPadding_left:none'> <div class='ipsPad'> <section class='ipsType_normal whiteBack'> <div class='ipsPageHeader ipsResponsive_pull ipsBox ipsPadding ipsSpacer_bottom'> <div class='ipsFlex ipsFlex-ai:center ipsFlex-fw:wrap ipsGap:4'> <div class='ipsFlex-flex:11'> <h1 class='ipsType_pageTitle ipsContained_container'> {{if $movie->mapped('locked')}} <span><span class="ipsBadge ipsBadge_icon ipsBadge_warning" data-ipsTooltip title='{lang="movies_locked"}'><i class='fa fa-lock'></i></span></span> {{endif}} {{if $movie->hidden() === 1}} <span><span class="ipsBadge ipsBadge_icon ipsBadge_warning" data-ipsTooltip title='{lang="pending_approval"}'><i class='fa fa-warning'></i></span></span> {{elseif $movie->hidden() === -1}} <span><span class="ipsBadge ipsBadge_icon ipsBadge_warning" data-ipsTooltip title='{$movie->hiddenBlurb()}'><i class='fa fa-eye-slash'></i></span></span> {{elseif $movie->hidden() === -2}} <span><span class="ipsBadge ipsBadge_icon ipsBadge_warning" data-ipsTooltip title='{$movie->deletedBlurb()}'><i class='fa fa-trash'></i></span></span> {{endif}} {{if $movie->canToggleItemModeration() and $movie->itemModerationEnabled()}} <span><span class="ipsBadge ipsBadge_icon ipsBadge_warning" data-ipsTooltip title='{lang="topic_moderation_enabled"}'><i class='fa fa-user-times'></i></span></span> {{endif}} {{if $movie->mapped('pinned')}} <span><span class="ipsBadge ipsBadge_icon ipsBadge_positive" data-ipsTooltip title='{lang="pinned"}'><i class='fa fa-thumb-tack'></i></span></span> {{endif}} {{if $movie->mapped('featured')}} <span><span class="ipsBadge ipsBadge_icon ipsBadge_positive" data-ipsTooltip title='{lang="featured"}'><i class='fa fa-star'></i></span></span> {{endif}} {{if $movie->canEdit()}} <span class='ipsType_break ipsContained' data-controller="core.front.core.moderation"> {{if $movie->locked()}}<i class='fa fa-lock'></i> {{endif}}<span data-role="editableTitle" title='{lang="click_hold_edit"}'>{$movie->title}</span> </span> {{else}} <span class='ipsType_break ipsContained'>{{if $movie->locked()}}<i class='fa fa-lock'></i> {{endif}}{$movie->title}</span> {{endif}} {{if $movie->status == 'Released'}} {{$date = \IPS\DateTime::ts( strtotime( $movie->release_date ) );}} <span class="jwse_movieYear">( {$date->format('Y')} )</span> {{endif}} {{if $fields['field_4']}} <span class="jwse_movieYear">( {{$fields = $movie->customFields();}}{$fields['field_4']} )</span> {{endif}} {{if $movie->prefix() OR ( $movie->canEdit() AND $movie::canTag( NULL, $movie->container() ) AND $movie::canPrefix( NULL, $movie->container() ) )}} <span {{if !$movie->prefix()}}class='ipsHide'{{endif}} {{if ( $movie->canEdit() AND $movie::canTag( NULL, $movie->container() ) AND $movie::canPrefix( NULL, $movie->container() ) )}}data-editablePrefix{{endif}}> {template="prefix" group="global" app="core" params="$movie->prefix( TRUE ), $movie->prefix()"} </span> {{endif}} </h1> <div class="jswe_movieMeta"> {{if $fields['field_6']}} <span class="ipsDataItem_generic certification">{{$fields = $movie->customFields();}}{$fields['field_6']|raw}</span> {{endif}} <!-- Movie Release Date --> {{if $movie->status == 'Released'}} {{$date = \IPS\DateTime::ts( strtotime( $movie->release_date ) );}} <span class="ipsDataItem_generic">{datetime="$date" dateonly="true"}</span> {{endif}} <!-- Movie Genres --> {{$genres = \IPS\movies\Movie::getGenreName( explode( ',', $movie->genres ) );}} <span class="ipsDataItem_generic">{$genres}</span> <!-- Movie Runtime --> {{if $movie->runtime}} {{$runtime = \IPS\movies\Movie::getRuntime( $movie->runtime );}} <span class="ipsDataItem_generic">{$runtime}</span> {{endif}} </div> {{if $movie->subtitle}} <span class="jwse_movieSubtitle">{$movie->subtitle}</span> {{endif}} <span class="jwse_movieDescription">{lang="meta_description"}</span> <p> {$movie->description|raw} </p> {{if \count( $movie->tags() ) OR ( $movie->canEdit() AND $movie::canTag( NULL, $movie->container() ) )}} {template="tags" group="global" app="core" params="$movie->tags(), FALSE, FALSE, ( $movie->canEdit() AND $movie::canTag( NULL, $movie->container() ) ) ? $movie->url() : NULL"} {{endif}} </div> </div> <hr class='ipsHr'> <div class='ipsPageHeader__meta ipsFlex ipsFlex-jc:between ipsFlex-ai:center ipsFlex-fw:wrap ipsGap:3'> <div class='ipsFlex-flex:11'></div> <div class='ipsFlex-flex:01 ipsResponsive_hidePhone'> <div class='ipsFlex ipsFlex-ai:center ipsFlex-jc:center ipsGap:3 ipsGap_row:0'> {{if \count( $movie->shareLinks() )}} {template="shareButton" app="core" group="sharelinks" params="$movie"} {{endif}} {template="promote" app="core" group="global" params="$movie"} {template="follow" app="core" group="global" params="'movies', 'movie', $movie->id, $movie->followersCount()"} </div> </div> </div> </div> {{if $movie->hidden() === 1 and $movie->canUnhide()}} <div class="ipsMessage ipsMessage_warning ipsSpacer_both"> <p class="ipsType_reset">{lang="movie_pending_approval"}</p> <br> <ul class='ipsList_inline'> <li><a href="{$movie->url()->csrf()->setQueryString( array( 'do' => 'moderate', 'action' => 'unhide' ) )}" class="ipsButton ipsButton_positive ipsButton_verySmall" title='{lang="approve_title_link"}'><i class="fa fa-check"></i> {lang="approve"}</a></li> {{if $movie->canDelete()}} <li><a href='{$movie->url()->csrf()->setQueryString( array( 'do' => 'moderate', 'action' => 'delete' ) )}' data-confirm title='{lang="movies_delete_title"}' class='ipsButton ipsButton_negative ipsButton_verySmall'><i class='fa fa-times'></i> {lang="delete"}</a></li> {{endif}} </ul> </div> {{endif}} <div class='ipsTabs ipsClearfix' id='elTabBar' data-ipsTabBar data-ipsTabBar-contentArea='#elTabContent' data-ipsTabBar-updateURL='false'> <a href='#elTabBar' data-action='expandTabs'><i class='fa fa-caret-down'></i></a> <ul role='tablist'> <li role='presentation'> <a href='{$movie->url()}' role='tab' id='elTabMovie' class='ipsTabs_item' aria-selected="true"> My Thoughts </a> </li> {{if \IPS\Settings::i()->movies_cast}} <li role='presentation'> <a href='{$movie->url()->setQueryString( array( 'do' => 'getMovieDataMovieView', 'type' => 'cast' ) )}' id='elTabCast' role='tab' class='ipsTabs_item'> {lang="movies_tab_cast"} </a> </li> {{endif}} {{if \IPS\Settings::i()->movies_crew}} <li role='presentation'> <a href='{$movie->url()->setQueryString( array( 'do' => 'getMovieDataMovieView', 'type' => 'crew' ) )}' id='elTabCrew' role='tab' class='ipsTabs_item'> {lang="movies_tab_crew"} </a> </li> {{endif}} {{if $movie->type == 'tv'}} <li role='presentation'> <a href='{$movie->url()->setQueryString( array( 'do' => 'getMovieDataMovieView', 'type' => 'seasons' ) )}' id='elTabEpisodes' role='tab' class='ipsTabs_item'> {lang="movies_tab_seasons"} </a> </li> {{endif}} {{if \IPS\Settings::i()->movies_videos}} <li role='presentation'> <a href='{$movie->url()->setQueryString( array( 'do' => 'getMovieDataMovieView', 'type' => 'videos' ) )}' id='elTabVideos' role='tab' class='ipsTabs_item'> {lang="movies_tab_videos"} </a> </li> {{endif}} {{if \IPS\Settings::i()->movies_posters}} <li role='presentation'> <a href='{$movie->url()->setQueryString( array( 'do' => 'getMovieDataMovieView', 'type' => 'posters' ) )}' id='elTabPosters' role='tab' class='ipsTabs_item'> {lang="movies_tab_posters"} </a> </li> {{endif}} {{if \IPS\Settings::i()->movies_images}} <li role='presentation'> <a href='{$movie->url()->setQueryString( array( 'do' => 'getMovieDataMovieView', 'type' => 'images' ) )}' id='elTabImages' role='tab' class='ipsTabs_item'> {lang="movies_tab_images"} </a> </li> {{endif}} {{if \IPS\Settings::i()->movies_similar}} <li role='presentation'> <a href='{$movie->url()->setQueryString( array( 'do' => 'getMovieDataMovieView', 'type' => 'similar' ) )}' id='elTabSimilar' role='tab' class='ipsTabs_item'> {{if $movie->type == 'movie'}} {lang="movies_tab_similar"} {{else}} {lang="movies_tab_similar_tv"} {{endif}} </a> </li> {{endif}} </ul> </div> <section id='elTabContent'> <div class="ipsBox ipsPad"> <div itemprop='text' data-controller='core.front.core.lightboxedImages'> <div class="jwse_MyReviewFace"></div> <div class="jwse_MyReviewText"> <span class="jwse_MyReviewTextRating"> <!-- JWSE Rating Circle --> {{if $fields['field_5']}} <div class="jwse_ratingCircle"> <svg viewBox="0 0 36 36" class="circular-chart orange"> <path class="circle-bg" d="M18 2.0845 a 15.9155 15.9155 0 0 1 0 31.831 a 15.9155 15.9155 0 0 1 0 -31.831" /> <path class="circle" stroke-dasharray="{{$fields = $movie->customFields();}}{$fields['field_5']|raw}, 100" d="M18 2.0845 a 15.9155 15.9155 0 0 1 0 31.831 a 15.9155 15.9155 0 0 1 0 -31.831" /> <text x="17" y="22.35" class="percentage">{{$fields = $movie->customFields();}}{$fields['field_5']|raw}</text> <text x="27" y="15.35" class="percentageSmall">%</text> </svg> </div> {{else}} <div class="jwse_ratingCircle"> <svg viewBox="0 0 36 36" class="circular-chart"> <path class="circle-bg" d="M18 2.0845 a 15.9155 15.9155 0 0 1 0 31.831 a 15.9155 15.9155 0 0 1 0 -31.831" /> <path class="circle" stroke-dasharray="0, 100" d="M18 2.0845 a 15.9155 15.9155 0 0 1 0 31.831 a 15.9155 15.9155 0 0 1 0 -31.831" /> <text x="18" y="19.35" class="percentage unrated">Unrated</text> </svg> </div> {{endif}} </span> {{if $fields['field_2']}} {{$fields = $movie->customFields();}}{$fields['field_2']|raw} {{else}} <p> <i>I have not written a review for this {{if $movie->type == 'movie'}}Movie{{else}}TV Show{{endif}} yet.<br /> Write a comment and remind me to write one!</i> </p> {{endif}} </div> <br style="clear:both;" /> </div> {{if $movie->container()->auto_content AND \IPS\Member::loggedIn()->language()->checkKeyExists( "movies_category_{$movie->container()->id}_append_content" )}} {{$lang = \IPS\Member::loggedIn()->language()->addToStack( "movies_category_{$movie->container()->id}_append_content" );}} {{\IPS\Member::loggedIn()->language()->parseOutputForDisplay( $lang );}} {{$content = str_replace( "%MOVIENAME%", $movie->title, $lang );}} {{\IPS\Member::loggedIn()->language()->parseOutputForDisplay( $content );}} {$content|raw} {{endif}} </div> </section> </section> </div> </article> </div> {{if ( $movie->canEdit() or $movie->canPin() or $movie->canUnpin() or $movie->canFeature() or $movie->canUnfeature() or $movie->canHide() or $movie->canUnhide() or $movie->canMove() or $movie->canLock() or $movie->canUnlock() or $movie->canDelete() ) or ( $movie->hidden() == -2 AND \IPS\Member::loggedIn()->modPermission('can_manage_deleted_content') ) or $movie->canReportOrRevoke() or ( \IPS\IPS::classUsesTrait( $movie, 'IPS\Content\Reactable' ) and settings.reputation_enabled )}} <div class='ipsItemControls'> {{if \IPS\IPS::classUsesTrait( $movie, 'IPS\Content\Reactable' ) and settings.reputation_enabled}} {template="reputation" app="core" group="global" params="$movie"} {{endif}} {{if ( $movie->canEdit() or $movie->canPin() or $movie->canUnpin() or $movie->canFeature() or $movie->canUnfeature() or $movie->canHide() or $movie->canUnhide() or $movie->canMove() or $movie->canLock() or $movie->canUnlock() or $movie->canDelete() ) or ( $movie->hidden() == -2 AND \IPS\Member::loggedIn()->modPermission('can_manage_deleted_content') ) or $movie->canReportOrRevoke()}} <ul class='ipsComment_controls ipsClearfix ipsItemControls_left'> {{if ( $movie->canEdit() or $movie->canPin() or $movie->canUnpin() or $movie->canFeature() or $movie->canUnfeature() or $movie->canHide() or $movie->canUnhide() or $movie->canMove() or $movie->canLock() or $movie->canUnlock() or $movie->canDelete() ) or ( $movie->hidden() == -2 AND \IPS\Member::loggedIn()->modPermission('can_manage_deleted_content') )}} <li> <a href='#elMovieActions_menu' id='elMovieActions' class='ipsButton ipsButton_light ipsButton_verySmall ipsButton_fullWidth' data-ipsMenu>{lang="{$movie->type}_actions"} <i class='fa fa-caret-down'></i></a> <ul id='elMovieActions_menu' class='ipsMenu ipsMenu_auto ipsHide'> {{if $movie->canReportOrRevoke() === TRUE}} <li class='ipsMenu_item'> <a href='{$movie->url('report')}' data-ipsDialog data-ipsDialog-size='medium' data-ipsDialog-title="{lang="report_movie"}" data-ipsDialog-remoteSubmit data-ipsDialog-flashMessage="{lang="report_submit_success"}" title="{lang="report_movie"}" >{lang="report"}</a> </li> <li class='ipsMenu_sep'><hr></li> {{endif}} {{if \IPS\Member::loggedIn()->modPermission('can_manage_deleted_content') AND $movie->hidden() == -2}} <li class='ipsMenu_item'><a href='{$movie->url()->csrf()->setQueryString( array( 'do' => 'moderate', 'action' => 'restore' ) )}' data-confirm data-confirmSubMessage='{lang="restore_as_visible_desc"}'>{lang="restore_as_visible"}</a></li> <li class='ipsMenu_item'><a href='{$movie->url()->csrf()->setQueryString( array( 'do' => 'moderate', 'action' => 'restoreAsHidden' ) )}' data-confirm data-confirmSubMessage='{lang="restore_as_hidden_desc"}'>{lang="restore_as_hidden"}</a></li> <li class='ipsMenu_item'><a href='{$movie->url()->csrf()->setQueryString( array( 'do' => 'moderate', 'action' => 'delete', 'immediate' => 1 ) )}' data-confirm data-confirmSubMessage='{lang="delete_immediately_desc"}'>{lang="delete_immediately"}</a></li> {{else}} {{if $movie->canEdit()}} <li class='ipsMenu_item'><a href='{$movie->url()->setQueryString( array( 'do' => 'edit' ) )}' title='{lang="edit"}'>{lang="edit"}</a></li> {{endif}} {{if $movie->canFeature()}} <li class='ipsMenu_item'><a href='{$movie->url()->csrf()->setQueryString( array( 'do' => 'moderate', 'action' => 'feature' ) )}'>{lang="feature"}</a></li> {{endif}} {{if $movie->canUnfeature()}} <li class='ipsMenu_item'><a href='{$movie->url()->csrf()->setQueryString( array( 'do' => 'moderate', 'action' => 'unfeature' ) )}'>{lang="unfeature"}</a></li> {{endif}} {{if $movie->canPin()}} <li class='ipsMenu_item'><a href='{$movie->url()->csrf()->setQueryString( array( 'do' => 'moderate', 'action' => 'pin' ) )}'>{lang="pin"}</a></li> {{endif}} {{if $movie->canUnpin()}} <li class='ipsMenu_item'><a href='{$movie->url()->csrf()->setQueryString( array( 'do' => 'moderate', 'action' => 'unpin' ) )}'>{lang="unpin"}</a></li> {{endif}} {{if $movie->canHide()}} <li class='ipsMenu_item'><a href='{$movie->url()->csrf()->setQueryString( array( 'do' => 'moderate', 'action' => 'hide' ) )}' data-ipsDialog data-ipsDialog-title="{lang="hide"}">{lang="hide"}</a></li> {{endif}} {{if $movie->canUnhide()}} <li class='ipsMenu_item'><a href='{$movie->url()->csrf()->setQueryString( array( 'do' => 'moderate', 'action' => 'unhide' ) )}'>{{if $movie->hidden() === 1}}{lang="approve"}{{else}}{lang="unhide"}{{endif}}</a></li> {{endif}} {{if $movie->canLock()}} <li class='ipsMenu_item'><a href='{$movie->url()->csrf()->setQueryString( array( 'do' => 'moderate', 'action' => 'lock' ) )}'>{lang="lock"}</a></li> {{endif}} {{if $movie->canUnlock()}} <li class='ipsMenu_item'><a href='{$movie->url()->csrf()->setQueryString( array( 'do' => 'moderate', 'action' => 'unlock' ) )}'>{lang="unlock"}</a></li> {{endif}} {{if $movie->canMove()}} <li class='ipsMenu_item'><a href='{$movie->url()->setQueryString( array( 'do' => 'move' ) )}' data-ipsDialog data-ipsDialog-size='narrow' data-ipsDialog-title="{lang="move"}">{lang="move"}</a></li> {{endif}} {{if $movie->canDelete()}} <li class='ipsMenu_item'><a href='{$movie->url()->csrf()->setQueryString( array( 'do' => 'moderate', 'action' => 'delete' ) )}' data-confirm>{lang="delete"}</a></li> {{endif}} {{if $movie->canOnMessage( 'add' )}} <li class='ipsMenu_item'><a href='{$movie->url()->csrf()->setQueryString( array( 'do' => 'messageForm' ) )}' data-ipsDialog data-ipsDialog-title='{lang="add_message"}'>{lang='add_message'}</a></li> {{endif}} {{if $movie->canToggleItemModeration()}} <li class='ipsMenu_item'><a href='{$movie->url()->csrf()->setQueryString( array( 'do' => 'toggleItemModeration' ) )}' data-confirm data-confirmMessage='{{if $movie->itemModerationEnabled()}}{lang="disable_movie_moderation_confirm"}{{else}}{lang="enable_movie_moderation_confirm"}{{endif}}'>{{if $movie->itemModerationEnabled()}}{lang="disable_topic_moderation"}{{else}}{lang="enable_topic_moderation"}{{endif}}</a></li> {{endif}} {{if \IPS\Member::loggedIn()->modPermission('can_reimport_data')}} <li class='ipsMenu_sep'><hr></li> <li class="ipsMenu_item"><a href='{$movie->url()->csrf()->setQueryString( array( 'do' => 'reimport' ) )}' data-confirm>{lang="movies_reimport_{$movie->type}_data"}</a></li> {{endif}} {{if \IPS\Member::loggedIn()->modPermission('can_view_moderation_log')}} <li class='ipsMenu_sep'><hr></li> <li class="ipsMenu_item"><a href='{$movie->url()->csrf()->setQueryString( array( 'do' => 'modLog' ) )}' data-ipsDialog data-ipsDialog-title='{lang="moderation_history"}'>{lang="moderation_history"}</a></li> {{endif}} {{endif}} </ul> </li> {{elseif $movie->canReportOrRevoke() === TRUE}} <li> <a href='{$movie->url('report')}' data-ipsDialog data-ipsDialog-size='medium' data-ipsDialog-title="{lang="report_file"}" data-ipsDialog-remoteSubmit data-ipsDialog-flashMessage="{lang="report_submit_success"}" title="{lang="report_file"}" class='ipsButton ipsButton_link ipsButton_verySmall ipsButton_fullWidth'>{lang="report"}</a> </li> {{endif}} </ul> {{endif}} </div> {{endif}} </div> <div class='ipsBox ipsPadding ipsResponsive_pull ipsResponsive_showPhone ipsMargin_top'> <div class='ipsGap_row:3'> <div> {template="follow" app="core" group="global" params="'movies', 'movie', $movie->id, $movie->followersCount()"} </div> {{if \count( $movie->shareLinks() )}} <div> {template="shareButton" app="core" group="sharelinks" params="$movie, 'verySmall', 'light'"} </div> {{endif}} <div> {template="promote" app="core" group="global" params="$movie"} </div> </div> </div> {{if $prev || $next}} <div class='ipsGrid ipsGrid_collapsePhone ipsPager ipsSpacer_top'> {{if $prev !== NULL}} <div class="ipsGrid_span6 ipsType_left ipsPager_prev"> <a href="{$prev->url()}" title="{lang="prev_movie"}" rel="prev"> <span class="ipsPager_type">{lang="prev_movie"}</span> <span class="ipsPager_title ipsType_light ipsType_break">{wordbreak="$prev->mapped('title')"}</span> </a> </div> {{else}} <div class="ipsGrid_span6 ipsType_left ipsPager_prev"> <a href="{$movie->container()->url()}" title="{lang="go_to_category" sprintf="$movie->container()->_title"}" rel="up"> <span class="ipsPager_type">{lang="movie_back_to_category"}</span> <span class="ipsPager_title ipsType_light ipsType_break">{lang="$movie->container()->_title" wordbreak="true"}</span> </a> </div> {{endif}} {{if $next !== NULL}} <div class="ipsGrid_span6 ipsType_right ipsPager_next"> <a href="{$next->url()}" title="{lang="next_movie"}" rel="next"> <span class="ipsPager_type">{lang="next_movie"}</span> <span class="ipsPager_title ipsType_light ipsType_break">{wordbreak="$next->mapped('title')"}</span> </a> </div> {{else}} <div class="ipsGrid_span6 ipsType_right ipsPager_next"> <a href="{$movie->container()->url()}" title="{lang="go_to_category" sprintf="$movie->container()->_title"}" rel="up"> <span class="ipsPager_type">{lang="movie_back_to_category"}</span> <span class="ipsPager_title ipsType_light ipsType_break">{lang="$movie->container()->_title" wordbreak="true"}</span> </a> </div> {{endif}} </div> <hr class='ipsHr'> {{endif}} {{if $commentsAndReviews}} <a id="replies"></a> <h2 class='ipsHide'>{lang="user_feedback"}</h2> <div class='ipsMargin_top'>{$commentsAndReviews|raw}</div> {{endif}} </div> </div> {{if $movie->container()->club()}} </div> {{endif}} <div class="ipsClear ipsClearfix"></div> </pre> </div> <div class="ipsTabs_panel" id="ipsTabs_elTabBar_2_panel"> <pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_9425_9" style=""> <span class="pln">Message</span></pre> </div> <div class="ipsTabs_panel" id="ipsTabs_elTabBar_3_panel"> <pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_9425_9" style=""> <span class="pln">Message</span></pre> </div> <div class="ipsTabs_panel" id="ipsTabs_elTabBar_4_panel"> <pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_9425_9" style=""> <span class="pln">Message </span></pre> </div> <div class="ipsTabs_panel" id="ipsTabs_elTabBar_5_panel"> <pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_9425_9" style=""> <span class="pln">Message</span></pre> </div> </section>  
  {{if \count( $movies )}} {{$rowCount=0;}} {{foreach $movies as $movie}} {{$rowCount++;}} <li class='ipsDataItem {{if $movie->unread()}}ipsDataItem_unread{{endif}} {{if method_exists( $movie, 'tableClass' ) && $movie->tableClass()}}ipsDataItem_{$movie->tableClass()}{{endif}} {{if $movie->hidden()}}ipsModerated{{endif}}' data-ipsLazyLoad> <div class='ipsDataItem_generic ipsDataItem_size3 ipsPos_top'> <!-- JWSE Rating Circle --> {{$fields = $movie->customFields();}} {{if $fields['field_5']}} <div class="jwse_ratingCircle jwse_ratingCircle-categoryList"> <svg viewBox="0 0 36 36" class="circular-chart circular-chart-categoryList orange" > <path class="circle-bg" d="M18 2.0845 a 15.9155 15.9155 0 0 1 0 31.831 a 15.9155 15.9155 0 0 1 0 -31.831" /> <path class="circle" stroke-dasharray="{{$fields = $movie->customFields();}}{$fields['field_5']|raw}, 100" d="M18 2.0845 a 15.9155 15.9155 0 0 1 0 31.831 a 15.9155 15.9155 0 0 1 0 -31.831" /> <text x="17" y="22.35" class="percentage">{{$fields = $movie->customFields();}}{$fields['field_5']|raw}</text> <text x="27" y="15.35" class="percentageSmall">%</text> </svg> </div> {{elseif $fields['field_5'] == 0}} <div class="jwse_ratingCircle jwse_ratingCircle-categoryList"> <svg viewBox="0 0 36 36" class="circular-chart circular-chart-categoryList"> <path class="circle-bg" d="M18 2.0845 a 15.9155 15.9155 0 0 1 0 31.831 a 15.9155 15.9155 0 0 1 0 -31.831" /> <path class="circle" stroke-dasharray="0, 100" d="M18 2.0845 a 15.9155 15.9155 0 0 1 0 31.831 a 15.9155 15.9155 0 0 1 0 -31.831" /> <text x="18" y="19.35" class="percentage unrated">Unrated</text> </svg> </div> {{endif}} {{if $movie->cover_thumb}} {template="coverPhoto" group="global" app="movies" params="$movie->cover_thumb, $movie->url(), $movie->title, 'medium'"} {{else}} <a class="sosMovieCover_medium" title='{lang="view_this" sprintf="$movie->title"}' href="{$movie->url()}"> <img src='{resource="movie.PNG" app="movies" location="front"}'> </a> {{endif}} </div> <div class='ipsDataItem_main'> <h3 class='ipsDataItem_title ipsType_center ipsType_sectionHead ipsContained_container'> {{if $movie->unread()}} <span><span class='ipsItemStatus ipsItemStatus_small' data-ipsTooltip title='{{if $movie->unread() === -1}}{lang="new"}{{else}}{lang="updated"}{{endif}}'><i class="fa fa-circle"></i></span>&nbsp;</span> {{endif}} {{if $movie->mapped('locked') || $movie->mapped('pinned') || $movie->affiliate || $movie->mapped('featured') || $movie->hidden() === -1 || $movie->hidden() === 1}} {{if $movie->mapped('locked')}} <span><span class="ipsBadge ipsBadge_small ipsBadge_icon ipsBadge_negative" data-ipsTooltip title='{lang="locked"}'><i class='fa fa-lock'></i></span></span> {{endif}} {{if $movie->hidden() === -1}} <span><span class="ipsBadge ipsBadge_icon ipsBadge_small ipsBadge_warning" data-ipsTooltip title='{$movie->hiddenBlurb()}'><i class='fa fa-eye-slash'></i></span></span> {{elseif $movie->hidden() === 1}} <span><span class="ipsBadge ipsBadge_icon ipsBadge_small ipsBadge_warning" data-ipsTooltip title='{lang="pending_approval"}'><i class='fa fa-warning'></i></span></span> {{endif}} {{if $movie->mapped('pinned')}} <span><span class="ipsBadge ipsBadge_icon ipsBadge_small ipsBadge_positive" data-ipsTooltip title='{lang="pinned"}'><i class='fa fa-thumb-tack'></i></span></span> {{endif}} {{if $movie->mapped('featured')}} <span><span class="ipsBadge ipsBadge_icon ipsBadge_small ipsBadge_positive" data-ipsTooltip title='{lang="featured"}'><i class='fa fa-star'></i></span></span> {{endif}} {{endif}} {{if $movie->prefix()}} <span>{template="prefix" group="global" app="core" params="$movie->prefix( TRUE ), $movie->prefix()"}</span> {{endif}} <span class='ipsType_break ipsContained'> <a href='{$movie->url()}' class='' title='{{if $movie->mapped('title')}}{$movie->mapped('title')}{{else}}{lang="content_deleted"}{{endif}} {{if $movie->canEdit()}}{lang="click_hold_edit"}{{endif}}' {{if $movie->canEdit()}} data-role="editableTitle"{{endif}} {{if $movie->tableHoverUrl and $movie->canView()}} data-ipsHover data-ipsHover-target='{$movie->url()->setQueryString('preview', 1)}' data-ipsHover-timeout='1.5'{{endif}}> <span> {{if $movie->mapped('title') or $movie->mapped('title') == 0}}{$movie->mapped('title')}{{else}}<em class="ipsType_light">{lang="content_deleted"}</em>{{endif}} </span> </a> </span> </h3> <p class='ipsType_light'>{lang="byline_nodate" htmlsprintf="$movie->author()->link()"}</p> <div class='ipsType_richText ipsType_normal ipsSpacer_both' data-ipsTruncate data-ipsTruncate-type="remove" data-ipsTruncate-size="2 lines"> {$movie->truncated(TRUE)|raw} </div> <p class='ipsType_reset ipsType_light ipsType_blendLinks'> {{if \IPS\Request::i()->app != 'movies'}} {lang="in"} <a href="{$movie->container()->url()}">{$movie->container()->_title}</a> {{endif}} </p> {{if \count( $movie->tags() )}} <div class=" ipsSpacer_top"> {template="tags" group="global" app="core" params="$movie->tags()"} </div> {{endif}} <p class='ipsType_reset'> <ul class="ipsList_inline"> {{if $movie->runtime}} {{$runtime = \IPS\movies\Movie::getRuntime( $movie->runtime );}} <li class='ipsType_light ipsType_blendLinks'>{lang="movie_running_{$movie->type}"}: {$runtime}</li> {{endif}} {{$genres = \IPS\movies\Movie::getGenreName( explode( ',', $movie->genres ) );}} <li class='ipsType_light ipsType_blendLinks'>{$genres}</li> </ul> </p> </div> <div class='ipsDataItem_generic ipsDataItem_size8'> {{if $movie->container()->allowcomments}} <p class='ipsType_normal {{if !$movie->comments}}ipsType_light{{endif}}'> {{if $movie->comments}} <a href='{$movie->url()->setQueryString( 'tab', 'comments' )->setFragment('replies')}'> {{endif}} <i class='fa fa-comment'></i> {lang="num_comments" pluralize="$movie->comments"} {{if $movie->comments}} </a> {{endif}} </p> {{endif}} {{if $movie->container()->allowreviews}} {{if $movie->reviews}} <a href='{$movie->url()->setQueryString( 'tab', 'reviews' )->setFragment('reviews')}'> {{endif}} {template="rating" group="global" location="front" app="core" params="'large', $movie->averageReviewRating(), \IPS\Settings::i()->reviews_rating_out_of, $movie->memberReviewRating()"}&nbsp;&nbsp; <span class='ipsType_normal ipsType_light'>({lang="num_reviews" pluralize="$movie->reviews"})</span> {{if $movie->reviews}} </a> {{endif}} {{endif}} <p class='ipsType_medium'><strong>{{if $movie->updated == $movie->submitted}}{lang="submitted"} {datetime="$movie->submitted"}{{else}}{lang="updated"} {datetime="$movie->updated"}{{endif}}</strong></p> </div> {{if method_exists( $table, 'canModerate' ) AND $table->canModerate()}} <div class='ipsDataItem_modCheck'> <span class='ipsCustomInput'> <input type='checkbox' data-role='moderation' name="moderate[{$movie->id}]" data-actions="{expression="implode( ' ', $table->multimodActions( $movie ) )"}" data-state='{{if $movie->tableStates()}}{$movie->tableStates()}{{endif}}'> <span></span> </span> </div> {{endif}} </li> {{endforeach}} {{endif}}  
<li class='ipsAreaBackground_reset ipsType_blendLinks ipsClearfix sosMovieIndexBlock sosMovieIndexBlockItem ipsPad_half ipsCarousel_item' data-ipsLazyLoad> <div class="indexBlock"> <div class='ipsPos_center'> <div class="sosMovieCover"> <!-- JWSE Rating Circle --> {{$fields = $movie->customFields();}} {{if $fields['field_5']}} <div class="jwse_ratingCircle jwse_ratingCircle-indexBlock"> <svg viewBox="0 0 36 36" class="circular-chart circular-chart-indexBlock orange" > <path class="circle-bg" d="M18 2.0845 a 15.9155 15.9155 0 0 1 0 31.831 a 15.9155 15.9155 0 0 1 0 -31.831" /> <path class="circle" stroke-dasharray="{{$fields = $movie->customFields();}}{$fields['field_5']|raw}, 100" d="M18 2.0845 a 15.9155 15.9155 0 0 1 0 31.831 a 15.9155 15.9155 0 0 1 0 -31.831" /> <text x="17" y="22.35" class="percentage">{{$fields = $movie->customFields();}}{$fields['field_5']|raw}</text> <text x="27" y="15.35" class="percentageSmall">%</text> </svg> </div> {{elseif $fields['field_5'] == 0}} <div class="jwse_ratingCircle jwse_ratingCircle-indexBlock"> <svg viewBox="0 0 36 36" class="circular-chart circular-chart-indexBlock"> <path class="circle-bg" d="M18 2.0845 a 15.9155 15.9155 0 0 1 0 31.831 a 15.9155 15.9155 0 0 1 0 -31.831" /> <path class="circle" stroke-dasharray="0, 100" d="M18 2.0845 a 15.9155 15.9155 0 0 1 0 31.831 a 15.9155 15.9155 0 0 1 0 -31.831" /> <text x="18" y="19.35" class="percentage unrated">Unrated</text> </svg> </div> {{endif}} <a href='{$movie->url()}' title='{lang="view_this_movie" sprintf="$movie->title"}'> <div class=""> {{if $movie->cover_thumb}} {template="bgCover" group="global" app="movies" params="$movie->cover_thumb, $movie->url(), $movie->title, 'big'"} {{else}} <a title='{lang="view_this" sprintf="$movie->title"}' href="{$movie->url()}"> <img class="bgMovieCover" src='{resource="movie.PNG" app="movies" location="front"}'> </a> {{endif}} </div> </a> <div class="jwse_MoviePhoto_Footer jwse_MoviePhoto_Footer-indexBlock"> {template="rating" group="global" location="front" app="core" params="'large', $movie->averageReviewRating(), \IPS\Settings::i()->reviews_rating_out_of, $movie->memberReviewRating()"}&nbsp;&nbsp; </div> </div> </div> </div> </li>  
<li class='ipsCarousel_item ipsAreaBackground_reset ipsPad' data-ipsLazyLoad> <div class='ipsColumns ipsColumns_collapsePhone'> <div class='ipsColumn ipsColumn_medium ipsType_center'> <!-- JWSE Rating Circle --> {{$fields = $movie->customFields();}} {{if $fields['field_5']|raw}} <div class="jwse_ratingCircle jwse_ratingCircle-indexBlock jwse_ratingCircle-Featured"> <svg viewBox="0 0 36 36" class="circular-chart circular-chart-indexBlock circular-chart-Featured orange" > <path class="circle-bg" d="M18 2.0845 a 15.9155 15.9155 0 0 1 0 31.831 a 15.9155 15.9155 0 0 1 0 -31.831" /> <path class="circle" stroke-dasharray="{{$fields = $movie->customFields();}}{$fields['field_5']|raw}, 100" d="M18 2.0845 a 15.9155 15.9155 0 0 1 0 31.831 a 15.9155 15.9155 0 0 1 0 -31.831" /> <text x="17" y="22.35" class="percentage">{{$fields = $movie->customFields();}}{$fields['field_5']|raw}</text> <text x="27" y="15.35" class="percentageSmall">%</text> </svg> </div> {{else}} <div class="jwse_ratingCircle jwse_ratingCircle-indexBlock jwse_ratingCircle-Featured"> <svg viewBox="0 0 36 36" class="circular-chart circular-chart-indexBlock circular-chart-Featured"> <path class="circle-bg" d="M18 2.0845 a 15.9155 15.9155 0 0 1 0 31.831 a 15.9155 15.9155 0 0 1 0 -31.831" /> <path class="circle" stroke-dasharray="0, 100" d="M18 2.0845 a 15.9155 15.9155 0 0 1 0 31.831 a 15.9155 15.9155 0 0 1 0 -31.831" /> <text x="18" y="19.35" class="percentage unrated">Unrated</text> </svg> </div> {{endif}} {template="coverPhoto" group="global" app="movies" params="$movie->cover_thumb, $movie->url(), $movie->title, 'large'"} </div> <div class='ipsColumn ipsColumn_fluid'> <h1 class='ipsType_pageTitle ipsContained_container'> {{if $movie->mapped('locked')}} <span><span class="ipsBadge ipsBadge_icon ipsBadge_warning" data-ipsTooltip title='{lang="movies_locked"}'><i class='fa fa-lock'></i></span></span> {{endif}} {{if $movie->hidden() === 1}} <span><span class="ipsBadge ipsBadge_icon ipsBadge_warning" data-ipsTooltip title='{lang="pending_approval"}'><i class='fa fa-warning'></i></span></span> {{elseif $movie->hidden() === -1}} <span><span class="ipsBadge ipsBadge_icon ipsBadge_warning" data-ipsTooltip title='{$movie->hiddenBlurb()}'><i class='fa fa-eye-slash'></i></span></span> {{elseif $movie->hidden() === -2}} <span><span class="ipsBadge ipsBadge_icon ipsBadge_warning" data-ipsTooltip title='{$movie->deletedBlurb()}'><i class='fa fa-trash'></i></span></span> {{endif}} {{if $movie->canToggleItemModeration() and $movie->itemModerationEnabled()}} <span><span class="ipsBadge ipsBadge_icon ipsBadge_warning" data-ipsTooltip title='{lang="topic_moderation_enabled"}'><i class='fa fa-user-times'></i></span></span> {{endif}} {{if $movie->mapped('pinned')}} <span><span class="ipsBadge ipsBadge_icon ipsBadge_positive" data-ipsTooltip title='{lang="pinned"}'><i class='fa fa-thumb-tack'></i></span></span> {{endif}} {{if $movie->mapped('featured')}} <span><span class="ipsBadge ipsBadge_icon ipsBadge_positive" data-ipsTooltip title='{lang="featured"}'><i class='fa fa-star'></i></span></span> {{endif}} {{if $movie->canEdit()}} <span class='ipsType_break ipsContained' data-controller="core.front.core.moderation"> {{if $movie->locked()}}<i class='fa fa-lock'></i> {{endif}}<span data-role="editableTitle" title='{lang="click_hold_edit"}'><a href='{$movie->url()}'>{$movie->title}</a></span> </span> {{else}} <span class='ipsType_break ipsContained'>{{if $movie->locked()}}<i class='fa fa-lock'></i> {{endif}}<a href='{$movie->url()}'>{$movie->title}</a></span> {{endif}} {{if $movie->status == 'Released'}} {{$date = \IPS\DateTime::ts( strtotime( $movie->release_date ) );}} <span class="jwse_movieYear">( {$date->format('Y')} )</span> {{endif}} {{if $fields['field_4']}} <span class="jwse_movieYear">( {{$fields = $movie->customFields();}}{$fields['field_4']} )</span> {{endif}} {{if $movie->prefix() OR ( $movie->canEdit() AND $movie::canTag( NULL, $movie->container() ) AND $movie::canPrefix( NULL, $movie->container() ) )}} <span {{if !$movie->prefix()}}class='ipsHide'{{endif}} {{if ( $movie->canEdit() AND $movie::canTag( NULL, $movie->container() ) AND $movie::canPrefix( NULL, $movie->container() ) )}}data-editablePrefix{{endif}}> {template="prefix" group="global" app="core" params="$movie->prefix( TRUE ), $movie->prefix()"} </span> {{endif}} </h1> <div class="jswe_movieMeta"> {{$fields = $movie->customFields();}} {{if $fields['field_6']}} <span class="ipsDataItem_generic certification">{{$fields = $movie->customFields();}}{$fields['field_6']|raw}</span> {{endif}} <!-- Movie Release Date --> {{if $movie->status == 'Released'}} {{$date = \IPS\DateTime::ts( strtotime( $movie->release_date ) );}} <span class="ipsDataItem_generic">{datetime="$date" dateonly="true"}</span> {{endif}} <!-- Movie Genres --> {{$genres = \IPS\movies\Movie::getGenreName( explode( ',', $movie->genres ) );}} <span class="ipsDataItem_generic">{$genres}</span> <!-- Movie Runtime --> {{if $movie->runtime}} {{$runtime = \IPS\movies\Movie::getRuntime( $movie->runtime );}} <span class="ipsDataItem_generic">{$runtime}</span> {{endif}} </div> <div class='ipsType_richText ipsType_normal ipsSpacer_both' data-ipsTruncate data-ipsTruncate-type="remove" data-ipsTruncate-size="2 lines"> {$movie->truncated(TRUE)|raw} </div> <p class='ipsType_reset'> <ul class="ipsList_inline"> {{$runtime = \IPS\movies\Movie::getRuntime( $movie->runtime );}} <li class='ipsType_light ipsType_blendLinks'>{lang="movie_running_{$movie->type}"}: {$runtime} </li> {{$genres = \IPS\movies\Movie::getGenreName( explode( ',', $movie->genres ) );}} <li class='ipsType_light ipsType_blendLinks'>{$genres} </li> </ul> </p> <ul class='ipsToolList ipsToolList_horizontal ipsSpacer_top'> <li class='ipsPos_left'> <a href='{$movie->url()}' class='ipsButton ipsButton_light ipsButton_fullWidth ipsButton_small' title='{lang="view_this_movie" sprintf="$movie->title"}'>{lang="more_information"} <i class="far fa-play-circle"></i></a> </li> </ul> </div> </div> </li>  
/*** Custom Styles ***/ .jwse_note{ color: #294459 } .ipsBadge_positive{ --badge--background: var(--color-palegrey); --badge--color: var(--color-jwsecoral); } /*** Youtube Popup ***/ .JWSE_video-container { overflow: hidden; position: relative; width:100%; } .JWSE_video-container::after { padding-top: 56.25%; display: block; content: ''; } .JWSE_video-container iframe { position: absolute; top: 0; left: 0; width: 100%; height: 100%; } .JWSE_play { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); text-align:center; height:170px; width:170px; background: rgb(41,68,89, 0.5); display:block; border-radius:100% } .JWSE_playBtn{ position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); font-size:70px; opacity:0.9; color:white; display:block; } .JWSE_playTxt{ position: absolute; top: 80%; left: 50%; transform: translate(-50%, -50%); font-size:16px; color:white; display:block; } /*** My Rating Circle ***/ .jwse_MyReviewText{ position:relative; } .jwse_ratingCircle { float: right; position: absolute; top: 0px; right: 0px; z-index:999; width:100px; } .jwse_ratingCircle-indexBlock { float: right; position: absolute; top: 0px; right: 0px; z-index:999; width:50px; } .jwse_ratingCircle-categoryList { float: left; position: absolute; top: -5px; left: 112px; z-index:999; width:50px; } .circular-chart{ display: block; margin: 10px auto; max-width: 80%; max-height: 100px; background-color: var(--color-baseblue); border-radius: 50%; } .circular-chart-indexBlock{ max-width:100%; max-height:50px; top:-6px; right: 3px; position:relative; } .circular-chart-Featured{ max-height:50px; top:-5px; right: 17px; position:relative; } .circular-chart_unrated{ background-color: var(--color-palegrey); } .circle-bg { fill: none; stroke: #eee; stroke-width: 3.8; opacity: 0.3; } .circle { fill: none; stroke-width: 3.8; stroke-linecap: round; animation: progress 1s ease-out forwards; } @keyframes progress { 0% { stroke-dasharray: 0 100; } } .circular-chart.orange .circle { stroke: var(--color-jwsecoral); } .percentage { fill: white; font-family: Montserrat; font-size: 13px; text-anchor: middle; } .percentageSmall{ fill: white; font-family: Montserrat; font-size: 5px; text-anchor: middle; } .unrated{ font-size:5px; } /*** View CSS ***/ .full-width { width: 100vw; position: relative; left: 50%; right: 50%; margin-left: -50vw; margin-right: -50vw; margin-top:-15px; min-height:800px; background-image:url(https:{media="528"}); background-color: #294459; background-position:top center; background-repeat:no-repeat; background-size:cover; } .ipsPageHeader.ipsClearfix.ipsSpacer_bottom { margin-top: -200px; } .ipsColumn_veryWide { width: 300px; } .ipsBox.ipsResponsive_pull { background-color: rgba(255, 255, 255, 0.6); } .whiteBack { font-size: 18px; background-color: white; border-radius:7px; } .ipsCarousel_inner { height: 230px !important; } .sosMovieCover_large img { margin: 0px auto; width: 100%; width: -moz-available; width: -webkit-fill-available; width: stretch; max-width: 300px !important; max-height: auto; border-top-right-radius: 7px; border-top-left-radius: 7px; } .jwse_MoviePhoto_Footer{ background-color: var(--color-baseblue); text-align:center; padding:15px 0; border-bottom-right-radius:7px; border-bottom-left-radius:7px; color:white !important; } .jwse_MoviePhoto_Footer-indexBlock{ position:absolute; display:block; width:100%; bottom:0; z-index:999; padding: 3px 0; margin-bottom:-28px; } .jwse_MoviePhoto_Footer-indexBlock .ipsRating.ipsRating_large{ font-size:10px !important; } .ipsRating .ipsRating_mine .ipsRating_on .fa-star { color: var(--color-jwsecoral) !important; } .jwse_movieYear{ font-size:16px; } .ipsDataItem_generic{ display: table-cell; padding: 5px 15px 15px 0; vertical-align: center; } .ipsDataItem_generic.certification { border: 1px solid #666; padding: 0 5px; display: block; float: left; margin: 5px; } .jwse_movieSubtitle{ font-weight:600; font-style: italic; display:block; } .jwse_MyReviewFace{ background-image:url(https:{media="530"}); background-size:cover; width:100px; height:100px; float:left; display:block; } .jwse_MyReviewTextRating { float: right; margin: 45px; } .jwse_MyReviewText{ margin-left:130px; background-color: var(--color-baseblue); color: white; font-size: 16px; padding:10px 30px; position:relative; border-radius:7px; } .jwse_MyReviewText:after { content: ''; position: absolute; left: 0; top: 30px; width: 0; height: 0; border: 20px solid transparent; border-right-color: var(--color-baseblue); border-left: 0; margin-top: -20px; margin-left: -20px; opacity:0.4; } .jwse_movieDescription { margin-top: 20px; display: block; font-size: 16px; font-weight: bold; margin-bottom: -10px; } .jwseHeroContainer { position: relative; top: 0px !important; } .ipsCarousel .ipsCarousel_inner { height: 230px !important; } #elMoviesFeatured .ipsCarousel .ipsCarousel_inner { height: 322px !important; } #elMoviesFeatured .ipsCarousel_item{ background-color:#FFF2CC; } #elMoviesFeatured .ipsButton { background-color: var(--color-baseblue) !important; color: white; }  
I hope this will help get you started building your own Movie database. If you use this for your own website, please drop a link here, so we can see how it turned out. Also if you need help, just drop a comment here or in the forum and I will try my best to help you.
Good luck and enjoy!
  • 35
  • 3547

Sweden stregthen their security - by establishing a national security center

By 💫 Jimi Wikman ·
Sweden is setting up a national security center. This was decided on December 10th by the government to commission Försvarets radioanstalt, Försvars­makten, Myndig­heten för samhälls­skydd och beredskap and Säkerhets­polisen to establish a national cyber security center. The purpose is to strengthen Sweden's overall ability to prevent, detect and manage cyber threats.
The government writes that the cyber threats against Sweden and Swedish interests are extensive. With technology development and digitalization, the threats and vulnerabilities increase, which means that security needs to be strengthened. The national cyber security center will contribute to making Sweden safer by increasing the overall ability to meet cyber threats and effectively support both public and private actors. This will contribute to strengthening security in society as a whole, the government believes.
Within the framework of the cyber security center, the authorities shall:
Coordinate work to prevent, detect and manage cyber attacks and other IT incidents. Provide advice and support regarding threats, vulnerabilities and risks. Provide a national platform for collaboration and information exchange with private and public actors in the field of cyber security. In total, the government estimates an investment of SEK 440 million in the cyber security center up to and including 2025.
2021 - 50 million SEK 2022 - 60 million SEK 2023 - 60 million SEK 2024 - 120 million SEK 2025 - 150 million SEK  
 

4 new feature for DevOps in Jira provide more visibility and better insights

By 💫 Jimi Wikman ·
One of the biggest cause for complicated workflows and custom fields is the fact that Jira has never been a deployment tool, but the teams need the visibility of deploys. With this latest update to Jira Software Cloud we see improvements in this field with no less than four new features. DevOps teams or not, this will make life easier for everyone.
While there are several add-ons for Jira Software Cloud that will tie together repositories with Jira, this is now a standard feature in Jira. That means that it can be used with no additional cost (except for the Premium features if you don't have Premium of course), which lowers the entry point substantially. Hopefully this will mean the end of operations based statuses in the workflow and custom fields to hold release data.
Code in Jira
This feature allows you to connect your repo to Jira so you can visually see the repos in a new tab in your project called Code. Doing so if fairly easy, especially if you use Bitbucket, but adding Github for example takes only a few minutes. The biggest benefit of this feature for me however is that you will see the repo information in the Jira issue itself. This provide a lot of information to the viewer on where this issue has been committed.

 
Deployments in Jira
This is where things get interesting.  With Code in Jira above you can see where your code packages are and their status. With Deployments, you can see where those packages also have been delivered to. This provides a very nice overview of where your code currently is available, which is what most people want to know when they try to build this into their workflows or work views.
 
Premium Features
The last two features are only available for the Premium version of Jira Software Cloud. Normally I don't like to have features split between a standard and a more expensive version, but these two features are all about measuring which I think is fine to have as a bonus if you need the more expensive version.
Deployment Frequency

 
Cycle Time

 
My Thoughts
This is a very good addition to Jira Software Cloud that have been needed for a long time. While I think the Premium features is mostly for numbers people and not really for the daily work I think this still is a solid addition. I would like to see the deployment information on the issues and it needs a connection with Insight, so I can connect deploys to my assets.
There is a lot of potential here and I look forward to seeing this taking further in the future.

How to make realistic estimates when working with software development

By 💫 Jimi Wikman ·
Estimations for software development seem to be the bane of almost every company, but why is that really? Why is it so difficult to approximate the time and effort required when we do this all the time? There are a few reasons and in this article I will go over how I do my estimations and give you some of my experience on how you can maintain a 90% accuracy in any project.
For a good estimate you need to start with one very important thing: define if you estimate based on time to complete or  time to deliver. These are very different because the time to deliver will be substantially higher and much more prone to variations. This is because in the daily work you will be dragged into meetings, have people distracting you and so on. For good estimations we always estimate on time to complete as our basis.
Stop doing happy estimates
The worst thing you can do is making happy estimates. These are estimates based on the utopia that everything will work smoothly and you will be left in total isolation to focus. It never happens and Murphy is always present. Happy estimates may seem good because the product owner love low estimates, but they hate when you fail to deliver.
Happy estimates usually happen when an architect is stressed and throw out a guesstimate on the fly. It will almost always bite you in the butt because not only will everything seem easy to the architect, so they will underestimate it, they also never consider Murphy.
You do not estimate to be nice, you estimate to give a realistic view on when something can be available in production. If you have a hard time to stop making happy estimates, then implement this very simple rule:
"Any time that is needed beyond the time you have estimated you will do in your own time without payment"
Take your time doing estimates
An estimate is not a guesstimate that you throw out on a high level requirement. An estimate is your promise on when you can deliver and as such you should take your time and think that through. Don't sit around doing arbitrary guess work in a planning poker or play with t-shirt sizes unless you want to avoid doing any estimates at all. That type of guesstimating is for high level requirements, so make sure they stay there.
Break down the requirement and use your experience to guide you to a first number in your estimation. This estimate is how long it will actually take to sit down and build based on the requirement. Make sure that you ask questions to clarify where needed so have the information you need. This is also a good time to start working on a solution design to make sure you have considered things that may not always be apparent. For example validation of data in integrations or extending a JavaScript validation for a new form.
When we have done this, the number is still wrong, but we need it as a starting point.
Add the things you forgot
Now that you have done your estimate, many think they are done. That is rarely the case, so let us add the things that is not included yet. The most obvious problem that I see a lot is that the estimates is usually done by the most experience person, which also usually is the one that work fastest of everyone in the team. So what we do is to look at the estimate and the team, then we adjust based on what the slowest person in the team think is appropriate. This way we know that the base estimate is reasonable for everyone on the team.
The second thing we do is to consider Murphy. Things always happen and based on the complexity we increase the estimate with 20-100%. Not having room for mistakes is in itself a very big mistake. It will almost always happen and if it does not happen for this particular task we will either be able to resolve things faster than expected, or we have more time for other tasks. Either way that is a positive thing.
The third thing to look at are testing. All code should have unit tests and it is often forgotten in the estimate. You also have non-functional requirements such as loading times and browser support that you must consider. As a developer you are also responsible for testing the code and this should be added as a second estimate. For many estimates this alone will add 20-200% or even more to the estimate. This is especially true for frontend development where you often have 18+ variations just for devices and browsers.
These three very simple things easily multiply the original estimate two to three times.
Now let us consider your efficiency!
A factor that is very often overlooked is efficiency. No person in any team will ever have 100% efficiency in the sprint. I do not mean your focus level during the day, even if that also is a factor, I am talking about those pesky things that break your concentration. Things like meetings, stand-ups, code reviews and all those "I just want to ask a quick question", cost a lot of your time during the day. In my experience most teams will have an efficiency of around 40-60% depending on how often they are disturbed.
Depending on if your product owner understand estimation or not you can choose if you add the efficiency in the estimate, or if you have that as an external factor in the sprint itself. If you are new to this part of the estimation I suggest that you start with 50% efficiency and then adjust over time as you get better at estimating your efficiency.
With this step we now add a second two time multiplier and you are now close to a realistic estimate.
This may seem like a lot
This may sound like the estimates always will be very high compared to how you are working today, and you are right. This is why you always feel stressed and why you fail to deliver on promise in every sprint. It is why we invent things like story points to mitigate our inability to take responsibility for things we can't estimate properly. Realistic estimates are just that and if your estimates are far lower than what you get doing estimates this way, then remember this very simple rule:
It is always better to overestimate and over deliver than to underestimate and under deliver.
 
The Quick Summary
Make estimations in actual time to complete, not arbitrary measurements. Take your time to understand the task at hand and stop guesstimating. Adjust the estimate to fit the slowest person on your team. Add task for testing and make estimation separate for that. Remember that things always go wrong, so make room for that. Make sure that your efficiency is considered and calculated into the time to deliver.
Making good estimates is based on experience and knowledge. This means that like other skills you can get better at it. If you constantly work with arbitrary measurements like story points and you constantly fail with no change to your estimation process, then you should stop doing that. Not only will you fail at a crucial part of your work, your inability to provide accurate estimates actually cause harm in the form of stress and frustration. It is up to you if you want to spend your time in constant failure or constantly provide estimates that are realistic and dependable.
Learning to make good estimates is not rocket science, just common sense and experience.
So start doing it today.
  • 1
  • 3788

The Failed Product Owner - do you even provide feedback?

By 💫 Jimi Wikman ·
Product owners often get blamed for not understanding Agile and for not providing clear requirements. Is this their fault though, or are you not providing the correct feedback to help them improve? Agile teams often work with the product owner absent, especially in the retrospectives. If that is true for your team, who is actually at fault then?
I hear this all the time. "The product owner" is absent or "the product owner can't give a straight answer on what I should do, it changes by the minute". This is especially prominent in project based organizations where Agile means that you just remove the requirements phase in your waterfall process to make things fall down to the development team faster. Agile becomes ad-hoc and chaotic and it is all the product owners fault. Right?
This is a problematic attitude and one that to me clearly means that the team is not really Agile, but still work as a receiver instead of the engine it should be. Although in most organizations there are two processes and the development team always is the receiver of strategic goals, they should not be a passive one.
Not being passive means that you put demands on what you receive.
How you receive a new business need, what information you need and how you work together with the product owner is not something that you passively sit around and complain about. It is the core of what the retrospectives are for! I would bet that in most teams where you have issues with the product owner you do not include that person in your retrospectives? Do you even provide feedback or set up activities to help the product owner improve together with you?
If the product owner is not included, then could the issue be with you and not the product owner?
The product owner is a part of your team. This means that you are responsible to speak up and ensure that everyone in the team is working in the way that best benefit the team. This means that you should provide feedback if the product owner is absent or if you do not get the information you need. This should be done in retrospective, just like you do it with every other feedback concerning your work process and collaboration in the team.
 
Easy to say, but our Product Owner does not care.
I know, this happens all the time. The product owner is tied up in meetings all day and just ignore your feedback. This is where you need to play hardball and provide empiric evidence that the issues you have is not your fault and that you have done your job to try to improve this. The First step is to go above the product owner and point out the issue to the people above. Sometimes that works, but it may not always be an option.
In the sprint planning you should be very clear on what information need to be present for you to accept a user story. Remember that once you put the user story in the sprint, you have accepted the user story and you are responsible for the consequence for poor requirements. Never accept a user story that is not clear enough for you to work on. It is the responsibility of the product owner to ensure the user story can be accepted by the development team.
If you use Jira then your next step will be to push back everything that is unclear to the product owner. This is done by assigning the product owner to the issue, put the issue in blocked status, or flag the issue if you do not have a blocked status and then comment saying that you wait for the product owner to respond. Once done, leave the issue and move to another issue. This will effectively remove the priority for the first issue as it will now be done after whatever issue you pickup next.
This will allow you to move responsibility to the product owner and point out the issues you are having. It also allows you to get statistics on waiting times the team have due to the product owner not doing their job.
 
Failure is on the team, not the product owner alone
Just as the entire team is at fault if the scrum master or a team member is dragging the team down, the same goes for product owners. You manage it through the retrospective and constructive feedback. Help the product owner to be the team member you need hem to be. If that fail, then you as a team will fail as well.
Make demands, just as you would for any other team member. Make sure their calendar have all stand ups booked, all retrospectives should be holy times and if they run around for meetings all the time, have them block time in their calendar for the team. You would not accept a developer to not do their job, so don't accept if the product owner is not doing theirs either.
At the end of the day, remember that no product owner want to be a bad one. They often get dragged into activities where their role is poorly defined, so they need help to define what you as a team demand of them. This will make it more clear to them how to prioritize their time, or find a replacement to make sure you get the team member you deserve and need.
Never sit silent with a clenched fist in your pocket.
Speak up and offer constructive solutions and you will be surprised how willing many are to solve the situation with you. Teams work together and since we still have not developed telepathy we need to verbally communicate and express our need to work better together.
Product owners are not outsiders, they are valued team members.
...or at least they should be.
  • 0
  • 1315

Salesforce acquire Slack in a staggering $27.7 billion deal

By 💫 Jimi Wikman ·
Salesforce announced on January 1st that they have entered into a definitive agreement to acquire the very popular communication and collaboration platform Slack. The price tag for this acquisition is a staggering 27.7 billion us dollars. This is the biggest software acquisition of 2020 and it will have a big impact on the communications market for 2021.
This is no small acquisition by any means and with Slack becoming and integrated part of the Salesforce Customer 360 suite it will make ripples across the communications and collaboration ecosystem for sure. Slack have been loosing ground to Microsoft's Teams in the last years and it will be interesting to see what the future holds for Slack with this acquisition.
For Salesforce, I think this is a great acquisition as it will add a very popular and useful communication service into their products. They also get a huge customer database with millions of Slack users that they can try to convert into Salesforce customers. Hopefully this will also make their Salesforce Customer 360 suite more attractive because they will need to bring in quite a few customers to cover that price tag.
For existing Slack users I don't think there will be much that will change in the short term. I assume Slack will still be available as a standalone product, just like for example Trello was when Atlassian purchased them. Speaking of Atlassian it will be interesting to see what this means as Atlassian have been closely collaborating with Slack since Atlassian cancelled their own chat based collaboration  tools Hipchat and Stride that was sold to Slack in February 2019.
This acquisition surprised me a bit to be honest, but from a business perspective it makes sense from both lack and Salesforce perspective I think. It will be interesting to see what this means and if we might finally see a proper enterprise version of Slack or if Slack will slowly phase into Salesforce products and vanish...
 
 
 
  • 0
  • 1539

Account

Navigation

Search

Search

Configure browser push notifications

Chrome (Android)
  1. Tap the lock icon next to the address bar.
  2. Tap Permissions → Notifications.
  3. Adjust your preference.
Chrome (Desktop)
  1. Click the padlock icon in the address bar.
  2. Select Site settings.
  3. Find Notifications and adjust your preference.