@Override
  protected String getTitleHtml(Context ctx, Object... args) {
    String apk_id = (String) args[0];
    String userid = (String) args[1];
    String username = (String) args[2];
    String appname = (String) args[3];
    String act = "分享";
    String returnString = "";

    StreamLogic stream = GlobalLogics.getStream();
    RecordSet es_shared_Users = stream.findWhoSharedApp(ctx, apk_id, 200);
    String a = es_shared_Users.joinColumnValues("source", ",");
    List<String> ll = StringUtils2.splitList(a, ",", true);
    if (!ll.contains(userid)) {
      act = "评论";
    }

    if (getSettingKey(ctx).equals(Constants.NTF_MY_APP_LIKE)) {
      // returnString = "您分享(评论)的<stream id="+streamid+">stream</stream>被<user
      // id="+userid+">"+username+"</user> like了";
      // 您"+act+"的
      returnString =
          nameLinks
              + "赞了应用:<a href=\"borqs://application/details?id="
              + apk_id
              + "\">"
              + appname
              + "</a>";
    }
    return returnString;
  }
 private void attachSubscribe(Context ctx, RecordSet recs) {
   Map<String, Integer> m =
       GlobalLogics.getConversation()
           .getEnabledByTargetIds(ctx, recs.joinColumnValues("circle_id", ","));
   for (Record rec : recs) {
     String circleId = rec.getString("circle_id");
     rec.put("subscribe", m.get(circleId));
   }
 }
  @WebMethod("circle/show")
  public RecordSet showCircles(QueryParams qp) {
    FriendshipLogic fs = GlobalLogics.getFriendship();
    GroupLogic group = GlobalLogics.getGroup();

    Context ctx = WutongContext.getContext(qp, false);
    String userId = qp.getString("user", ctx.getViewerIdString());

    boolean withPublicCircles = qp.getBoolean("with_public_circles", false);
    if (!withPublicCircles) {
      RecordSet recs =
          fs.getCircles(
              ctx, userId, qp.getString("circles", ""), qp.getBoolean("with_users", false));
      attachSubscribe(ctx, recs);
      return recs;
    } else {
      String circleIds = qp.getString("circles", "");
      boolean withMembers = qp.getBoolean("with_users", false);
      List<String> circles = StringUtils2.splitList(circleIds, ",", true);
      List<String> groups = group.getGroupIdsFromMentions(ctx, circles);
      circles.removeAll(groups);
      RecordSet recs =
          fs.getCirclesP(ctx, userId, StringUtils2.joinIgnoreBlank(",", circles), withMembers);
      RecordSet recs0 =
          group.getGroups(
              ctx,
              PUBLIC_CIRCLE_ID_BEGIN,
              PUBLIC_CIRCLE_ID_END,
              ctx.getViewerIdString(),
              StringUtils2.joinIgnoreBlank(",", groups),
              GROUP_LIGHT_COLS + ",circle_ids,formal,subtype,parent_id",
              withMembers);
      recs0.renameColumn(GRP_COL_ID, "circle_id");
      recs0.renameColumn(GRP_COL_NAME, "circle_name");
      for (Record rec : recs) rec.put("type", CIRCLE_TYPE_LOCAL);
      for (Record rec : recs0) rec.put("type", CIRCLE_TYPE_PUBLIC);
      recs.addAll(recs0);

      attachSubscribe(ctx, recs);
      return recs;
    }
  }
  @TraceCall
  @Override
  public RecordSet searchPages(Context ctx, String kw, int page, int count) {
    String skw = "%" + ObjectUtils.toString(kw) + "%";

    String sql =
        new SQLBuilder.Select()
            .select("page_id")
            .from(pageTable)
            .where(
                "destroyed_time=0 AND (name LIKE ${v(kw)} OR name_en LIKE ${v(kw)}) OR address LIKE ${v(kw)} OR address_en LIKE ${v(kw)}",
                "kw",
                skw)
            .page(page, count)
            .toString();

    SQLExecutor se = getSqlExecutor();
    RecordSet pageIdRecs = se.executeRecordSet(sql, null);
    long[] pageIds = CollectionUtils2.toLongArray(pageIdRecs.getIntColumnValues("page_id"));
    return getPages(ctx, pageIds);
  }
  @Override
  public List<Long> getScope(Context ctx, String senderId, RecordSet recs, Object... args) {
    List<Long> userIds = new ArrayList<Long>();

    List<String> reasons = new ArrayList<String>();
    reasons.add(String.valueOf(Constants.C_STREAM_TO));
    reasons.add(String.valueOf(Constants.C_STREAM_ADDTO));
    ConversationLogic conversation = GlobalLogics.getConversation();
    RecordSet conversation_users =
        conversation.getConversation(
            ctx, Constants.POST_OBJECT, (String) args[0], reasons, 0, 0, 100);
    for (Record r : conversation_users) {
      long userId = Long.parseLong(r.getString("from_"));
      if (!userIds.contains(userId)) {
        if ((userId >= Constants.PUBLIC_CIRCLE_ID_BEGIN) && (userId <= Constants.GROUP_ID_END)) {
          groups.add(userId);
          GroupLogic groupImpl = GlobalLogics.getGroup();
          String members = groupImpl.getAllMembers(ctx, userId, -1, -1, "");
          List<Long> memberIds = StringUtils2.splitIntList(members, ",");
          userIds.addAll(memberIds);

          if (recs == null) {
            recs = new RecordSet();
          }
          RecordSet notifRecs = groupImpl.getMembersNotification(ctx, userId, members);
          recs.addAll(notifRecs);

          for (Record notifRec : notifRecs) {
            String memberId = notifRec.getString("member");
            long recvNotif = notifRec.getInt("recv_notif", 0);
            if (recvNotif == 2) {
              userIds.remove(Long.parseLong(memberId));
            }
          }
        } else userIds.add(userId);
      }
    }
    // =========================new send to ,from conversation end ====================

    // exclude sender
    if (StringUtils.isNotBlank(senderId)) {
      userIds.remove(Long.parseLong(senderId));
    }
    try {
      IgnoreLogic ignore = GlobalLogics.getIgnore();
      userIds = ignore.formatIgnoreUserListP(ctx, userIds, "", "");
    } catch (Exception e) {
    }

    return userIds;
  }
  @Override
  protected List<Long> getScope(Context ctx, String senderId, Object... args) {
    List<Long> userIds = new ArrayList<Long>();
    RecordSet liked_Users = new RecordSet();
    if (getSettingKey(ctx).equals(Constants.NTF_MY_APP_LIKE)) {
      /*
          RecordSet es_shared_Users = p.findWhoSharedApp((String)args[0], 200);
          for (Record ue : es_shared_Users) {
          if (!userIds.contains(Long.parseLong(ue.getString("source"))) && !ue.getString("source").equals("0") && !ue.getString("source").equals("")) {
              userIds.add(Long.parseLong(ue.getString("source")));
          }


          liked_Users = p.likedUsers(Constants.APK_OBJECT, (String)args[0], 0, 200);
      }
      */

      // =========================new send to ,from conversation=========================

      String packageName = (String) args[0].toString().trim();
      String[] a = StringUtils.split(packageName, "-");
      if (a.length > 1) packageName = a[0];

      List<String> reasons = new ArrayList<String>();
      reasons.add(String.valueOf(Constants.C_APK_SHARE));
      ConversationLogic conversation = GlobalLogics.getConversation();
      RecordSet conversation_users =
          conversation.getConversation(ctx, Constants.APK_OBJECT, packageName, reasons, 0, 0, 100);
      for (Record r : conversation_users) {
        if (!userIds.contains(Long.parseLong(r.getString("from_"))))
          userIds.add(Long.parseLong(r.getString("from_")));
      }

      List<String> reasons1 = new ArrayList<String>();
      reasons1.add(String.valueOf(Constants.C_APK_LIKE));
      RecordSet conversation_users1 =
          conversation.getConversation(ctx, Constants.APK_OBJECT, packageName, reasons1, 0, 0, 100);

      AccountLogic account = GlobalLogics.getAccount();
      liked_Users.addAll(
          account.getUsers(
              ctx,
              (String) args[1],
              conversation_users1.joinColumnValues("from_", ","),
              "user_id,display_name",
              false));

      // =========================new send to ,from conversation end ====================
    }
    if (userIds.contains(Long.parseLong((String) args[1])))
      userIds.remove(Long.parseLong((String) args[1]));

    // exclude sender
    if (StringUtils.isNotBlank(senderId)) {
      userIds.remove(Long.parseLong(senderId));
    }

    try {
      IgnoreLogic ignore = GlobalLogics.getIgnore();
      userIds = ignore.formatIgnoreUserListP(ctx, userIds, "", "");
      List<Long> lu = new ArrayList<Long>();
      for (Record r : liked_Users) {
        lu.add(Long.parseLong(r.getString("user_id")));
      }
      lu = ignore.formatIgnoreUserListP(ctx, lu, "", "");
      for (int i = liked_Users.size() - 1; i >= 0; i--) {
        if (!lu.contains(Long.parseLong(liked_Users.get(i).getString("user_id"))))
          liked_Users.remove(i);
      }
    } catch (Exception e) {
    }

    List<String> l = new ArrayList<String>();
    int i = 0;
    for (Record user : liked_Users) {
      if (i > 3) break;

      String userId = user.getString("user_id");
      String displayName = user.getString("display_name");
      if (StringUtils.contains(displayNames, displayName)) {
        continue;
      } else {
        displayNames += displayName + ", ";
        l.add(
            "<a href=\"borqs://profile/details?uid=" + userId + "&tab=2\">" + displayName + "</a>");
        i++;
      }
    }

    nameLinks = StringUtils.join(l, ", ");
    if (StringUtils.isNotBlank(displayNames)) {
      displayNames = StringUtils.substringBeforeLast(displayNames, ",");
    }

    return userIds;
  }
  private void attachDetailInfo(Context ctx, Record pageRec) {
    long associatedId = pageRec.getInt("associated_id", 0L);
    Record rec = Commons.getUnifiedUser(ctx, associatedId);
    rec.copyColumn(GRP_COL_ID, "circle_id");
    rec.copyColumn(GRP_COL_NAME, "circle_name");
    String freeCircleIds = pageRec.getString("free_circle_ids");
    RecordSet freeCircleRecs;
    if (StringUtils.isBlank(freeCircleIds)) {
      freeCircleRecs = new RecordSet();
    } else {
      freeCircleRecs =
          GlobalLogics.getGroup()
              .getGroups(
                  ctx,
                  Constants.PUBLIC_CIRCLE_ID_BEGIN,
                  Constants.PUBLIC_CIRCLE_ID_END,
                  ctx.getViewerIdString(),
                  freeCircleIds,
                  Constants.GROUP_LIGHT_COLS,
                  false);
    }

    freeCircleRecs.copyColumn(GRP_COL_ID, "circle_id");
    freeCircleRecs.copyColumn(GRP_COL_NAME, "circle_name");
    pageRec.put("associated", rec);
    pageRec.put("free_circles", freeCircleRecs);

    long[] followerIds =
        GlobalLogics.getFriendship()
            .getFollowerIds(ctx, ctx.getViewerId(), 0, Constants.GROUP_ID_BEGIN, 0, 15);
    RecordSet followerUserRecs =
        GlobalLogics.getAccount()
            .getUsers(
                ctx,
                ctx.getViewerIdString(),
                StringUtils2.join(followerIds, ","),
                AccountLogic.USER_LIGHT_COLUMNS,
                true);
    followerUserRecs.retainsCount(5);
    pageRec.put("followers", followerUserRecs);
    int objType = Constants.getUserTypeById(associatedId);
    if (objType == Constants.PUBLIC_CIRCLE_OBJECT) {
      boolean b =
          GlobalLogics.getGroup()
              .hasRight(ctx, associatedId, ctx.getViewerId(), Constants.ROLE_MEMBER);
      pageRec.put("in_associated_circle", b);
    } else {
      pageRec.put("in_associated_circle", false);
    }

    // shared count
    String viewerId = ctx.getViewerIdString();
    String pageId = pageRec.getString("page_id");
    StreamLogic stream = GlobalLogics.getStream();
    Record sharedCount = new Record();
    int sharedText = stream.getSharedCount(ctx, viewerId, pageId, TEXT_POST);
    sharedCount.put("shared_text", sharedText);
    int sharedPhoto = stream.getSharedCount(ctx, viewerId, pageId, PHOTO_POST);
    sharedCount.put("shared_photo", sharedPhoto);
    int sharedBook = stream.getSharedCount(ctx, viewerId, pageId, BOOK_POST);
    sharedCount.put("shared_book", sharedBook);
    int sharedApk = stream.getSharedCount(ctx, viewerId, pageId, APK_POST);
    sharedCount.put("shared_apk", sharedApk);
    int sharedLink = stream.getSharedCount(ctx, viewerId, pageId, LINK_POST);
    sharedCount.put("shared_link", sharedLink);
    int shared_static_file = stream.getSharedCount(ctx, viewerId, pageId, FILE_POST);
    sharedCount.put("shared_static_file", shared_static_file);
    int shared_audio = stream.getSharedCount(ctx, viewerId, pageId, AUDIO_POST);
    sharedCount.put("shared_audio", shared_audio);
    int shared_video = stream.getSharedCount(ctx, viewerId, pageId, VIDEO_POST);
    sharedCount.put("shared_video", shared_video);
    sharedCount.put(
        "shared_poll", GlobalLogics.getPoll().getRelatedPollCount(ctx, viewerId, pageId));

    String eventIds = GlobalLogics.getGroup().getPageEvents(ctx, Long.parseLong(pageId));
    Set<String> set = StringUtils2.splitSet(eventIds, ",", true);
    sharedCount.put("shared_event", set.size());

    pageRec.put("shared_count", sharedCount);
  }